-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
512 lines (295 loc) · 618 KB
/
atom.xml
File metadata and controls
512 lines (295 loc) · 618 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
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>VioletJack 技术日志</title>
<subtitle>专注!坚持!求真!</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://violetjack.github.io/"/>
<updated>2019-02-15T17:21:09.585Z</updated>
<id>https://violetjack.github.io/</id>
<author>
<name>VioletJack</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>从 Vue 转 React 的一些体验</title>
<link href="https://violetjack.github.io/2019/02/15/vue-react/"/>
<id>https://violetjack.github.io/2019/02/15/vue-react/</id>
<published>2019-02-14T16:00:00.000Z</published>
<updated>2019-02-15T17:21:09.585Z</updated>
<content type="html"><![CDATA[<blockquote><p>原本打算自己写一写 Vue 转 React 的体验的,但是掘金上一搜发现一些不错的文章。这里就不进行技术细节上的对比了,只说下体验。</p></blockquote><h1 id="使用-Vue-和-React-的区别"><a href="#使用-Vue-和-React-的区别" class="headerlink" title="使用 Vue 和 React 的区别"></a>使用 Vue 和 React 的区别</h1><p>如果对于两者的区别感兴趣可以看下下面两篇文章:</p><ul><li><a href="https://juejin.im/post/5c2de832f265da6172659b45" target="_blank" rel="noopener">「Vue」与「React」–使用上的区别</a></li><li><a href="https://juejin.im/post/5b8b56e3f265da434c1f5f76" target="_blank" rel="noopener">个人理解Vue和React区别</a></li></ul><p>看了上面两篇文章简单整理出以下几点区别:</p><ol><li>Vue 使用 setter/getter 实现双向绑定,React 根据数据渲染页面。</li><li>Vue 使用 props 向子组件通信,使用 events 向父组件通信。React 通过 props 传递数据和回调函数进行父子组件间通信。</li><li>Vue 使用 mixins 而 React 使用 HOC 高阶组件组合功能。</li><li>Vue 使用 provide/inject 进行跨层级组件通信,React 通过 context 进行跨层级组件通信。</li><li>模板渲染方式上,Vue 通过 v-if/v-show/v-for 语法糖封装的逻辑来渲染,而 React 通过原生 JavaScript 代码逻辑来渲染。</li><li>状态管理上,Vuex 和 Redux 思路差不多。但是 Vuex 是双向绑定的,状态可变;而 Redux 的数据是不可变的,采取整体替换的方式更新状态。</li><li>Vue 中的 computed 和 watch 属性在 React 中并没有,需要手写逻辑控制或者使用外部工具,如 mobx。</li><li>Vue 使用 slot 标签来实现组件嵌套,而 React 通过将需要嵌套的组件当做 props 传给子组件的方式来实现嵌套。</li><li>Vue 和 React 的生命周期必然是不同的。</li></ol><h1 id="写-React-的体验"><a href="#写-React-的体验" class="headerlink" title="写 React 的体验"></a>写 React 的体验</h1><p>之前写惯了 Vue,在写 React 的时候给我几个感受:</p><ul><li>相比于 Vue 提供的一整套语法糖来实现渲染,React 的对渲染细节的控制能力比 Vue 好,更自由。</li><li>相比于 Vue 的类 HTML 写法让人看着很自然很舒服,React 的函数式编程让整体代码显得有些乱,应该是我还不习惯函数式编程的缘故把。</li><li>React 的社区真的很活跃,所以相关框架和库非常多。就我接手的两个 React 项目分别用到了 dva、umi、mobx 等不同的框架和库,而 Vue 大多数都是用的 Vue 提供的全家桶来进行开发。</li><li>ant design 真的很棒,据说有一个团队在维护它,相对而言 Vue 的几个组件库的功能数量和组件质量上就稍微差一些了。</li><li>在 React 中用到了像 class、注解等很多新特性,感觉很高级。Vue 项目并没有加这些新特性。由于 React 的自由度,社区把各种新语法都在 React 上玩起来了。</li><li>虽然大家各种对比前端三大框架,其实底层逻辑还是 JavaScript、HTML 和 CSS。我最近学习的 this、箭头函数、class、原型、ES6特性等知识点对我上手 React 的确有很大的帮助。所以说万变不离其宗,学好基础和底层逻辑最重要。</li></ul><h1 id="教训"><a href="#教训" class="headerlink" title="教训"></a>教训</h1><ol><li>遇到新技术一定仔细阅读完文档,可以避免很多不必要的坑。比如我在写 React 项目的时候花了一天时间写了一个组件,结果回头一看 antd 里面有差不多一样的 demo,实现的还比我好。</li><li>解决类似问题的技术的技术逻辑必然也是类似的,通过类比联想的方式学习相似技术可以提高不少的效率。</li><li>遇到问题要仔细阅读文档、Google 查找,在确认没有答案之后再询问他人,随意的打断他人问一些低级问题是非常不礼貌、不专业的。</li></ol><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>个人认为,如果前端基础足够扎实,上手这些框架都并非难事,只要熟悉了框架以及相关库的用法就可以得心应手了。</p>]]></content>
<summary type="html">
<blockquote>
<p>原本打算自己写一写 Vue 转 React 的体验的,但是掘金上一搜发现一些不错的文章。这里就不进行技术细节上的对比了,只说下体验。</p>
</blockquote>
<h1 id="使用-Vue-和-React-的区别"><a href="#使
</summary>
<category term="React" scheme="https://violetjack.github.io/tags/React/"/>
</entry>
<entry>
<title>JavaScript 类型全知道</title>
<link href="https://violetjack.github.io/2019/02/14/type/"/>
<id>https://violetjack.github.io/2019/02/14/type/</id>
<published>2019-02-13T16:00:00.000Z</published>
<updated>2019-02-15T17:21:09.584Z</updated>
<content type="html"><![CDATA[<blockquote><p>今天来聊聊 JavaScript 的类型。</p></blockquote><h1 id="JavaScript-的七大基本类型"><a href="#JavaScript-的七大基本类型" class="headerlink" title="JavaScript 的七大基本类型"></a>JavaScript 的七大基本类型</h1><ul><li>undefined 未定义</li><li>null 空值</li><li>boolean 布尔值</li><li>string 字符串</li><li>number 数字</li><li>object 对象</li><li>symbol 符号(ES6)</li></ul><p>怎么知道是有这么七个值呢,使用 typeof 运算符来查看。</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-43607c1c53f7dca8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="typeof"></p><p>上图中,大多数都如我们想的那样,但是有两个特例:</p><ol><li>可以看到 <code>typeof null</code> 理论上应该返回 <code>'null'</code> 但却返回的是 <code>'object'</code>,这是一个存在20多年由来已久的bug,所以要判断对象是否为 null 时需要注意。</li><li>当我们打印 <code>typeof function() {}</code> 的时候返回的类型是 <code>'function'</code>,是不是说明 function 也是基本类型呢?但其实 function 是 object 的子集,下面说引用类型的时候会提到。</li></ol><h1 id="null、undefined-和-undeclared"><a href="#null、undefined-和-undeclared" class="headerlink" title="null、undefined 和 undeclared"></a>null、undefined 和 undeclared</h1><p>在 JavaScript 的类型中有三种表示变量“不存在”的方式,null、undefined 和 undeclared。那么它们的区别是什么呢?看代码~</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-354f1c86c7c9d36e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="三种空"></p><ul><li>我们定义了变量 a 却没有给他赋值,所以 a 就是 undefined</li><li>我们没有定义变量 b,所以报错 b is not defined,我们称之为 undeclared。注意这和 undefined 是有区别的。</li><li>我们定义了变量 c 并给他赋值 null,所以 b 就是 null</li></ul><p>小结下:undefined 是定义了变量却没有赋值;undeclared 是没有定义变量更没有赋值,会报错;null 是定义了变量并且赋值空值 null。</p><h1 id="JavaScript-中的那些引用类型"><a href="#JavaScript-中的那些引用类型" class="headerlink" title="JavaScript 中的那些引用类型"></a>JavaScript 中的那些引用类型</h1><p>所有的引用类型都是 Object。我猜测由于在 JavaScript 中对于 Object 的访问是引用形式的,所以称之为引用类型。</p><h2 id="Object"><a href="#Object" class="headerlink" title="Object"></a>Object</h2><p>Object 是一个函数,它可以用于创建对象,也可以用它带的 API 方法操作对象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建对象</span></span><br><span class="line"><span class="keyword">var</span> obj = <span class="keyword">new</span> <span class="built_in">Object</span>(); <span class="comment">// 不推荐</span></span><br><span class="line"><span class="keyword">var</span> obj2 = {}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Object API</span></span><br><span class="line"><span class="keyword">var</span> obj3 = <span class="built_in">Object</span>.create(<span class="literal">null</span>)</span><br><span class="line"><span class="keyword">var</span> object2 = <span class="built_in">Object</span>.freeze(object1);</span><br></pre></td></tr></table></figure><p>为什么可以使用这些 API?因为在 Object 函数的原型(关于原型可以看之前的文章)中有定义这些 API 方法。</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-2ecc22f6b5162ed1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="api 方法在这里"></p><p>其他更多的 Object API 可以查阅 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object" target="_blank" rel="noopener">Object - JavaScript | MDN</a>。</p><h2 id="Array"><a href="#Array" class="headerlink" title="Array"></a>Array</h2><p>同样的,Array 函数用于创建数组和提供操作数组的 API。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// create array</span></span><br><span class="line"><span class="keyword">var</span> arr1 = []</span><br><span class="line"><span class="keyword">var</span> arr2 = <span class="keyword">new</span> <span class="built_in">Array</span>()</span><br><span class="line"><span class="keyword">var</span> arr3 = <span class="built_in">Array</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">// api</span></span><br><span class="line"><span class="built_in">Array</span>.isArray(arr1);</span><br><span class="line">arr1.push(<span class="number">1</span>)</span><br></pre></td></tr></table></figure><p>同样的,推荐查阅 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array" target="_blank" rel="noopener">Array - JavaScript | MDN</a> 获取更多 API 信息。</p><h2 id="Date"><a href="#Date" class="headerlink" title="Date"></a>Date</h2><p>Data 用于创建时间对象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> now = <span class="keyword">new</span> <span class="built_in">Date</span>()</span><br></pre></td></tr></table></figure><p>Date 函数通过传递不同的参数在生产不同的时间对象,参考 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date" target="_blank" rel="noopener">Date - JavaScript | MDN</a>。</p><p>注:在 ES6 中可以通过静态方法 Date.now() 来获取当前时间。</p><h2 id="RegExp"><a href="#RegExp" class="headerlink" title="RegExp"></a>RegExp</h2><p>RegExp 构造函数创建了一个正则表达式对象,用于将文本与一个模式匹配。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> regex1 = <span class="regexp">/\w+/</span>; </span><br><span class="line"><span class="keyword">var</span> regex2 = <span class="keyword">new</span> <span class="built_in">RegExp</span>(<span class="string">'\\w+'</span>); <span class="comment">// 不推荐</span></span><br></pre></td></tr></table></figure><h2 id="Function"><a href="#Function" class="headerlink" title="Function"></a>Function</h2><p>Function 构造函数 创建一个新的Function对象。 在 JavaScript 中, 每个函数实际上都是一个 Function 对象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> e = <span class="keyword">new</span> <span class="built_in">Function</span>( <span class="string">"a"</span>, <span class="string">"return a * 2;"</span> ); <span class="comment">// 不推荐</span></span><br><span class="line"><span class="keyword">var</span> f = <span class="function"><span class="keyword">function</span>(<span class="params">a</span>) </span>{ <span class="keyword">return</span> a * <span class="number">2</span>; }</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">g</span>(<span class="params">a</span>) </span>{ <span class="keyword">return</span> a * <span class="number">2</span>; }</span><br></pre></td></tr></table></figure><h2 id="包装器对象(Boolean、String-和-Number)"><a href="#包装器对象(Boolean、String-和-Number)" class="headerlink" title="包装器对象(Boolean、String 和 Number)"></a>包装器对象(Boolean、String 和 Number)</h2><blockquote><p>Boolean、String 和 Number 分别是基本类型 boolean、string 和 number 的包装器对象,有很多共性,所以就拿来一起讲了。</p></blockquote><h3 id="创建值"><a href="#创建值" class="headerlink" title="创建值"></a>创建值</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> bool = <span class="literal">true</span></span><br><span class="line"><span class="keyword">var</span> bool2 = <span class="built_in">Boolean</span>(<span class="literal">true</span>) <span class="comment">// 不推荐</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> str = <span class="string">'hello world'</span></span><br><span class="line"><span class="keyword">var</span> str2 = <span class="built_in">String</span>(<span class="string">'hello world'</span>) <span class="comment">// 不推荐</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> num = <span class="number">100</span></span><br><span class="line"><span class="keyword">var</span> num2 = <span class="built_in">Number</span>(<span class="number">100</span>) <span class="comment">// 不推荐</span></span><br></pre></td></tr></table></figure><p>可以看到,第二种创建方式非常的画蛇添足,但是这种写法可以有别的用处。</p><h3 id="自动包装"><a href="#自动包装" class="headerlink" title="自动包装"></a>自动包装</h3><p>上面我们定义了三个不同基本类型的变量,这几个变量后面可以加一些方法来进行操作。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">bool.toString();</span><br><span class="line">str.split(<span class="string">' '</span>)</span><br><span class="line">num..toFixed(<span class="number">2</span>)</span><br></pre></td></tr></table></figure><p>为什么明明基本类型没有这些属性和方法却可以使用呢?<br>这就要提到这三个基本类型的自动包装特性了。即虽然这三个基本类型没有属性,但是当我们调用其属性和函数时,会自动包装成相应的对象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Boolean</span>(bool).toString()</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">String</span>(str).split(<span class="string">' '</span>)</span><br><span class="line"><span class="keyword">new</span> <span class="built_in">Number</span>(num).toFixed(<span class="number">2</span>)</span><br></pre></td></tr></table></figure><p>可以看下 String 的原型中的确包含了 string 类型用到的所有的属性和方法。</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-1e612c6730b31699.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="string"></p><h3 id="强制类型转换"><a href="#强制类型转换" class="headerlink" title="强制类型转换"></a>强制类型转换</h3><p>对象包装器另外一个作用 —— 强制类型转换:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Boolean</span>(<span class="string">''</span>); <span class="comment">// false</span></span><br><span class="line"><span class="built_in">Boolean</span>(<span class="number">1</span>); <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">String</span>(<span class="literal">null</span>); <span class="comment">// 'null'</span></span><br><span class="line"><span class="built_in">String</span>(<span class="number">12138</span>); <span class="comment">// '12138'</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Number</span>(<span class="string">"123"</span>) <span class="comment">// 123</span></span><br><span class="line"><span class="built_in">Number</span>(<span class="string">""</span>) <span class="comment">// 0</span></span><br></pre></td></tr></table></figure><p>直接使用包装器函数就可以对值进行强制类型转换行为。</p><h2 id="Global"><a href="#Global" class="headerlink" title="Global"></a>Global</h2><p>Global 表示全局对象,具体的内容暂时没有找到,先贴一段网上的解释:</p><blockquote><p>《JavaScript高级程序设计》中谈到,global对象可以说是ECMAScript中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。从某种意义上讲,它是一个终极的“兜底儿对象”,换句话说呢,就是不属于任何其他对象的属性和方法,最终都是它的属性和方法。我理解为,这个global对象呢,就是整个JS的“老祖宗”,找不到归属的那些“子子孙孙”都可以到它这里来认祖归宗。所有在全局作用域中定义的属性和函数,都是global对象的属性和方法,比如isNaN()、parseInt()以及parseFloat()等,实际都是它的方法;还有就是常见的一些特殊值,如:NaN、undefined等都是它的属性,以及一些构造函数Object、Array等也都是它的方法。总之,记住一点:global对象就是“老祖宗”,所有找不到归属的就都是它的。</p></blockquote><h2 id="Math"><a href="#Math" class="headerlink" title="Math"></a>Math</h2><p>Math 是一个内置对象, 它具有数学常数和函数的属性和方法。不是一个函数对象。举几个栗子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Math</span>.abs(<span class="number">-1</span>); <span class="comment">// 取绝对值 1</span></span><br><span class="line"><span class="built_in">Math</span>.max(<span class="number">10</span>, <span class="number">20</span>); <span class="comment">// 取最大值 20</span></span><br><span class="line"><span class="built_in">Math</span>.random(); <span class="comment">// 取随机数</span></span><br></pre></td></tr></table></figure><h2 id="Symbol"><a href="#Symbol" class="headerlink" title="Symbol"></a>Symbol</h2><p>Symbol 函数是 ES6 添加的,用于表示有唯一性的特殊值。创建方法如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="built_in">Symbol</span>(<span class="string">'this is a symbol'</span>)</span><br></pre></td></tr></table></figure><h2 id="Error"><a href="#Error" class="headerlink" title="Error"></a>Error</h2><p>Error 函数用于创建错误对象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> err1 = <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'I was created using a function call!'</span>)</span><br><span class="line"><span class="keyword">var</span> err2 = <span class="built_in">Error</span>(<span class="string">'I was created using a function call!'</span>)</span><br></pre></td></tr></table></figure><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>本文先是介绍了七大基本类型,之后有简单介绍了 JavaScript 中的引用类型。主要想达到归纳整理的作用,让大家知道都有哪些类型,而具体使用中则强烈推荐查阅 <a href="https://developer.mozilla.org/zh-CN/" target="_blank" rel="noopener">MDN</a> 来使用各种方法和属性。</p><p>希望本文对你有所帮助~明天聊聊我最近从 Vue 转 React 项目的一些体会,明天见!</p>]]></content>
<summary type="html">
<blockquote>
<p>今天来聊聊 JavaScript 的类型。</p>
</blockquote>
<h1 id="JavaScript-的七大基本类型"><a href="#JavaScript-的七大基本类型" class="headerlink" title="
</summary>
<category term="JavaScript 基础" scheme="https://violetjack.github.io/tags/JavaScript-%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>聊聊对象行为委托</title>
<link href="https://violetjack.github.io/2019/02/13/behavior/"/>
<id>https://violetjack.github.io/2019/02/13/behavior/</id>
<published>2019-02-12T16:00:00.000Z</published>
<updated>2019-02-15T17:21:09.583Z</updated>
<content type="html"><![CDATA[<blockquote><p>昨天我们聊到了对象的原型,知道了有 new 和 Object.create() 两种操作原型的方式。今天我们来对比下使用这两种方式进行面向对象编程的特点。</p></blockquote><h1 id="使用-new-关键字写面向类"><a href="#使用-new-关键字写面向类" class="headerlink" title="使用 new 关键字写面向类"></a>使用 new 关键字写面向类</h1><p>先来一段面向类的代码实现</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span>(<span class="params">who</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.me = who;</span><br><span class="line">}</span><br><span class="line">Foo.prototype.identify = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"I am "</span> + <span class="keyword">this</span>.me;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Bar</span>(<span class="params">who</span>) </span>{</span><br><span class="line"> Foo.call(<span class="keyword">this</span>, who);</span><br><span class="line">}</span><br><span class="line">Bar.prototype = <span class="built_in">Object</span>.create(Foo.prototype);</span><br><span class="line">Bar.prototype.speak = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Hello, "</span> + <span class="keyword">this</span>.identify() + <span class="string">"."</span>);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b1 = <span class="keyword">new</span> Bar(<span class="string">"b1"</span>);</span><br><span class="line"><span class="keyword">var</span> b2 = <span class="keyword">new</span> Bar(<span class="string">"b2"</span>);</span><br><span class="line"></span><br><span class="line">b1.speak();</span><br><span class="line">b2.speak();</span><br></pre></td></tr></table></figure><p>当然也可以用 ES6 的 class 语法糖。class 的出现避免了在函数的 <code>prototype</code> 上添加属性的奇怪的行为。</p><h1 id="混乱不堪的原型关系"><a href="#混乱不堪的原型关系" class="headerlink" title="混乱不堪的原型关系"></a>混乱不堪的原型关系</h1><p>无论是 function 还是 class,其背后还是逃不开对于 prototype 的操作。而原型中各种关系令人头疼。下面是这段代码的关系图:</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-b78fe4bbd7d6532a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="原型关系"></p><p>抛开函数与 Function 和 Object 的关系,我们来捋一捋代码中的逻辑。</p><ol><li>Foo 函数的 prototype 上创建了函数 identify</li><li>Foo 函数的 prototype 引用了 Object 的原型(原型链理论)</li><li>Bar 函数 prototype 引用了以 Foo 的 prototype 为原型创建的对象</li><li>Bar 函数的 prototype 上创建了函数 speak</li><li>以 Bar 为构造器分别 new 了两个对象 b1 和 b2,b1 和 b2 的原型引用了 Bar 函数的 prototype</li><li>由于 Bar 进行了修改原型的操作,所以没有 constructor 函数。</li><li>所以根据原型链理论,Bar 的 prototype 和 b1、b2 的 constructor 都指向了 Foo 函数的 constructor。</li></ol><p>就这么个逻辑(这还没有包括函数与对象之前的关系),我表示我讨厌在 prototype 上去添加属性,这显得非常乱。</p><p>最后输出的 b1 对象的原型结构如下:</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-1cd13125458907a5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="原型结构"></p><h1 id="行为委托闪亮登场"><a href="#行为委托闪亮登场" class="headerlink" title="行为委托闪亮登场"></a>行为委托闪亮登场</h1><p>可以看到上面的写法导致指向非常混乱,实际上我们并不需要了解这么多逻辑。下面介绍一种方式完全不管 prototype 真正面向对象的写法 —— 行为委托。</p><p>就拿上面的例子来说:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">Foo = {</span><br><span class="line"> init: <span class="function"><span class="keyword">function</span> (<span class="params">who</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.me = who;</span><br><span class="line"> },</span><br><span class="line"> identify: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"I am "</span> + <span class="keyword">this</span>.me;</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Bar = <span class="built_in">Object</span>.create(Foo);</span><br><span class="line">Bar.speak = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> alert(<span class="string">"Hello, "</span> + <span class="keyword">this</span>.identify() + <span class="string">"."</span>);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b1 = <span class="built_in">Object</span>.create(Bar);</span><br><span class="line">b1.init(<span class="string">"b1"</span>);</span><br><span class="line"><span class="keyword">var</span> b2 = <span class="built_in">Object</span>.create(Bar);</span><br><span class="line">b2.init(<span class="string">"b2"</span>);</span><br><span class="line"></span><br><span class="line">b1.speak();</span><br><span class="line">b2.speak();</span><br></pre></td></tr></table></figure><p>其中的关键就是完全使用对象来写面向对象编程,而避免使用 new 一个构造器的写法。这里我们也来理一理逻辑:</p><ol><li>创建 Foo 对象,它包含 init 和 identify 两个函数属性。</li><li>创建 Bar 对象原型继承 Foo 对象。</li><li>在 Bar 对象上添加 speak 方法。</li><li>创建 b1 和 b2 对象原型继承 Bar 对象。</li><li>使用原型链上的 init 函数为 b1 和 b2 对象传入数据。</li><li>通过原型链调用 speak 函数。</li></ol><p>这种写法的原型关系图就简单了很多:</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-bdc4c082cbe29864.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="原型关系"></p><p>可以看到与第一中方法比少了函数构造器,少了函数构造器就没了 constructor 和 prototype。完全通过对象与对象之间的原型继承引用关系来实现面向对象的编程思想。</p><p>最后看下输出的 b1 对象的原型结构:</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-20caa15116450797.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="原型结构"></p><h1 id="行为委托的好处"><a href="#行为委托的好处" class="headerlink" title="行为委托的好处"></a>行为委托的好处</h1><ul><li>行为委托避免使用 new 构造器形式来实现面向对象,减少了大量构造器所带出的复杂关系。</li><li>行为委托只使用对象之间的原型继承关系,让整个代码逻辑变得非常清晰。</li></ul><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>这里介绍了行为委托这一种面向对象的设计思想,它让面向对象编程变得更加简洁、更加自然。</p><p>当然,这只是一种设计方式。如果执意要用 new 写法来写面向对象编程当然没有问题,推荐使用 class 语法糖,它可以将操作 prototype 的行为给隐藏起来,这使得代码更像 Java(引用对象的特性并未改变,所以只是看着像),从而让代码更好理解。</p><p>赶快去试试行为委托吧,我认为它是种很适合 JavaScript 的设计模式。</p><p>明天我们聊聊 JavaScript 的类型~</p>]]></content>
<summary type="html">
<blockquote>
<p>昨天我们聊到了对象的原型,知道了有 new 和 Object.create() 两种操作原型的方式。今天我们来对比下使用这两种方式进行面向对象编程的特点。</p>
</blockquote>
<h1 id="使用-new-关键字写面向类"><a h
</summary>
<category term="JavaScript 基础" scheme="https://violetjack.github.io/tags/JavaScript-%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>聊聊 JavaScript 的原型</title>
<link href="https://violetjack.github.io/2019/02/12/prototype/"/>
<id>https://violetjack.github.io/2019/02/12/prototype/</id>
<published>2019-02-11T16:00:00.000Z</published>
<updated>2019-02-15T17:21:09.583Z</updated>
<content type="html"><![CDATA[<blockquote><p>JavaScript 中的原型也是一个非常让人头疼的东西,很多前端同学对此也是一知半解,比如我。今天我们就好好捋一捋这个原型。</p></blockquote><h1 id="创建对象的方式"><a href="#创建对象的方式" class="headerlink" title="创建对象的方式"></a>创建对象的方式</h1><p>下面就是创建对象的几种方式:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> o1 = {</span><br><span class="line"> a: <span class="number">123</span>,</span><br><span class="line"> b: <span class="string">'hello world'</span></span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(o1.b)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun2</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.a = <span class="number">33</span></span><br><span class="line"> <span class="keyword">this</span>.b = <span class="string">'hello o2'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> o2 = <span class="keyword">new</span> fun2()</span><br><span class="line"><span class="built_in">console</span>.log(o2.b)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Fun3</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">this</span>.a = <span class="number">365</span></span><br><span class="line"> <span class="keyword">this</span>.b = <span class="string">'hello class'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> o3 = <span class="keyword">new</span> Fun3()</span><br><span class="line"><span class="built_in">console</span>.log(o3.b)</span><br></pre></td></tr></table></figure><p>有人说这是三种创建方式,但是我认为其实是两种创建方式(因为 class 语法糖的本质还是 function):<strong>直接定义对象</strong>和<strong>使用 new 关键词构造对象</strong>。</p><h1 id="原型和原型链"><a href="#原型和原型链" class="headerlink" title="原型和原型链"></a>原型和原型链</h1><p>当我们创建了一个对象之后,就产生了原型(<code>Object.create(null)</code> 是特例)。</p><h2 id="prototype-和-proto-的区别"><a href="#prototype-和-proto-的区别" class="headerlink" title="prototype 和 __proto__ 的区别"></a>prototype 和 <code>__proto__</code> 的区别</h2><p><code>__proto__</code> 是一个非正式的属性,很多环境中不支持该属性。它指向当前对象的原型。如下图:</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-1002cf3d69276266.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="__proto__"></p><p>上面的代码是一段原型继承,可以看到对象 obj1 继承了对象 obj,所以 obj1 的 <code>__proto__</code> 就指向了 obj,而 obj 的 <code>__proto__</code> 则指向了 Object。所有对象的原型链最终都将指向 Object。</p><p>而关于 prototype 我摘录了一段话:</p><blockquote><p>当你创建函数时,JS 会为这个函数自动添加 <code>prototype</code> 属性,值是一个有 constructor 属性的对象。而一旦你把这个函数当作构造函数(<code>constructor</code>)调用(即通过<code>new</code>关键字调用),那么 JS 就会帮你创建该构造函数的实例,实例继承构造函数 <code>prototype</code> 的所有属性和方法。</p></blockquote><p><img src="https://upload-images.jianshu.io/upload_images/1987062-c600260dc91c2314.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="prototype"></p><p>可以看到,对象 bar 的 <code>__proto__</code> 属性指向了函数 func 的 <code>prototype</code>。</p><p>总结下,<code>__proto__</code> 指向原型,而 <code>prototype</code> 是函数独有且构造的对象原型指向 <code>prototype</code>。</p><h2 id="理解原型链"><a href="#理解原型链" class="headerlink" title="理解原型链"></a>理解原型链</h2><p>每个对象都是原型,而对象之间是可以继承的。所以就产生了原型链。看图说话:</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-aa7441f2c85bb6f5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="原型链"></p><p>很好理解了,我们创建了四个对象逐层进行原型继承。最后打印 obj3 对象可以看到 <code>obj3 -> obj2 -> obj1 -> obj -> Object</code> 这就是原型链。</p><p>如果我要在 obj3 对象上访问 a 属性,那么 JavaScript 就会顺着原型链逐层往下找,最终在 obj 对象上找到了a 属性,这就是原型链查找数据的方式。如果找到 Object 也没有找到属性就返回 <code>undefined</code>。</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-07ac0815c99f5d7d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="原型链查找"></p><h1 id="为对象指定原型的两种方式"><a href="#为对象指定原型的两种方式" class="headerlink" title="为对象指定原型的两种方式"></a>为对象指定原型的两种方式</h1><p>那么如何为对象添加原型呢?</p><h2 id="1-new-关键字"><a href="#1-new-关键字" class="headerlink" title="1. new 关键字"></a>1. new 关键字</h2><p>第一种就是通过构造器的方式来创建。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.a = <span class="number">11</span></span><br><span class="line"> <span class="keyword">this</span>.b = <span class="number">22</span></span><br><span class="line">}</span><br><span class="line">Foo.prototype.c = <span class="number">33</span></span><br><span class="line">Foo.prototype.func = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'hello'</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> f = <span class="keyword">new</span> Foo()</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(f)</span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Object</span>.getPrototypeOf(f))</span><br></pre></td></tr></table></figure><p>当然,不得不说的是 ES6 的 class 语法糖写法:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">this</span>.a = <span class="number">11</span></span><br><span class="line"> <span class="keyword">this</span>.b = <span class="number">22</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> func() {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'hello'</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> f = <span class="keyword">new</span> Foo()</span><br></pre></td></tr></table></figure><p>两者其实是一样的效果,但是 class 写法更接近常规的类写法。(终于可以让 function 回归它原本的作用上了。)</p><h2 id="2-Object-create-obj-面向对象"><a href="#2-Object-create-obj-面向对象" class="headerlink" title="2. Object.create(obj) 面向对象"></a>2. Object.create(obj) 面向对象</h2><p>Object.create() 可以很好的实现原型继承行为,也能通过 Object API 来修改原型:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = { <span class="attr">a</span>: <span class="number">123</span>, <span class="attr">b</span>: <span class="number">456</span> }</span><br><span class="line"><span class="built_in">Object</span>.setPrototypeOf(obj, { <span class="attr">c</span>: <span class="number">789</span> })</span><br><span class="line"><span class="keyword">var</span> obj2 = <span class="built_in">Object</span>.create(obj)</span><br><span class="line">obj2.e = <span class="number">555</span></span><br></pre></td></tr></table></figure><p>代码输出结果如下图,的确实现了为对象指定原型的行为。</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-b592d69f559248af.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="原型继承和修改"></p><h1 id="引用流还是复制流?"><a href="#引用流还是复制流?" class="headerlink" title="引用流还是复制流?"></a>引用流还是复制流?</h1><p>使用 JavaScript 原型是特别要主义的一个点是:<strong>JavaScript 对于原型的继承是一种引用行为</strong>,即所引用的对象改变,继承对象的原型也会改变。</p><p>与之相反的,有些语言会使用复制的方式。即在原型继承时复制一份原型到当前对象,从此被复制的对象和复制对象再无瓜葛。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>随着 Object.create() 等一系列新 API 和 ES6 的 class 写法的出现,使用 function 作为构造器并使用 prototype 来修改原型的方式将逐渐被抛弃。但是由于历史原因这部分知识还是要理解其中原理的。</p><p>而 <code>__proto__</code> 属性是非正式属性,不适合在通用场景下使用。</p><p>而对于原型的写法,我认为有两种不错的处理方式:</p><blockquote><ol><li>完全使用 class 构造器写法来替代使用 function 构造器的写法来进行<strong>面向类</strong>的开发方式。</li><li>放弃原型写法,使用 Object 系列 API 进行<strong>面向对象</strong>的开发(行为委托就是这样的方式)。</li></ol></blockquote><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>关于原型,先聊这么多。明天我们聊聊基于 Object API 来实现的面向对象模式 —— 行为委托,敬请期待。</p>]]></content>
<summary type="html">
<blockquote>
<p>JavaScript 中的原型也是一个非常让人头疼的东西,很多前端同学对此也是一知半解,比如我。今天我们就好好捋一捋这个原型。</p>
</blockquote>
<h1 id="创建对象的方式"><a href="#创建对象的方式" class=
</summary>
<category term="JavaScript 基础" scheme="https://violetjack.github.io/tags/JavaScript-%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>聊聊 JavaScript 的 this</title>
<link href="https://violetjack.github.io/2019/02/11/this/"/>
<id>https://violetjack.github.io/2019/02/11/this/</id>
<published>2019-02-10T16:00:00.000Z</published>
<updated>2019-02-15T17:21:09.584Z</updated>
<content type="html"><![CDATA[<p>JavaScript 中的 this 一直是比较让人头疼,也是面试特别容易问及的问题。下面就参照这《你不知道的 JavaScript》来学习下 this 这个神奇的东西。</p><h1 id="this-到底指向何处"><a href="#this-到底指向何处" class="headerlink" title="this 到底指向何处"></a>this 到底指向何处</h1><blockquote><p>this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。 this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。</p></blockquote><p>所以,this 并不只是简单地指向函数或者对象自身。</p><h1 id="this-的四种绑定方式"><a href="#this-的四种绑定方式" class="headerlink" title="this 的四种绑定方式"></a>this 的四种绑定方式</h1><h2 id="默认绑定"><a href="#默认绑定" class="headerlink" title="默认绑定"></a>默认绑定</h2><p>所谓的默认绑定就是 this 的默认绑定方式。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">foo(); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure><p><strong>注意:</strong>严格模式下这种默认绑定形式不成立。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"><span class="meta"> "use strict"</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">foo(); <span class="comment">// TypeError</span></span><br></pre></td></tr></table></figure><h2 id="隐式绑定"><a href="#隐式绑定" class="headerlink" title="隐式绑定"></a>隐式绑定</h2><p>隐式绑定是指 this 所在函数在有<strong>上下文</strong>的前提下的绑定,如 <code>obj.foo();</code>。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">2</span>,</span><br><span class="line"> foo: foo</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">obj.foo(); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p><strong>注意:</strong>对象中的函数只是引用关系,即对象和函数存在于两个地方。所以在别的地方使用函数,与隐式绑定的对象就没有关系了。看下两个例子:</p><ol><li>其中 <code>var myFoo = obj.foo</code> 的 myFoo 变量引用的是 foo() 函数,与 obj 并无关系。所以 myFoo 的执行函数行为就变成了默认绑定,打印结果为 1。</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">2</span>,</span><br><span class="line"> foo: foo</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> myFoo = obj.foo</span><br><span class="line">myFoo(); <span class="comment">// 默认绑定,值为 1</span></span><br></pre></td></tr></table></figure><ol><li>在回调函数中其实也会出现 this 绑定丢失的情况,回调函数 obj.foo 引用的是 foo 函数,与 obj 对象并无关系。</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">2</span>,</span><br><span class="line"> foo: foo</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">setTimeout(obj.foo, <span class="number">300</span>); <span class="comment">// 1</span></span><br></pre></td></tr></table></figure><h2 id="显示绑定"><a href="#显示绑定" class="headerlink" title="显示绑定"></a>显示绑定</h2><p>显式绑定就是指使用 call、apply、bind 来指定某个上下文进行绑定,它们的一个作用就只为函数硬绑定一个上下文对象。</p><p>之前的回调函数使用 bind 进行修改后打印出了我们 obj 对象中的 a 属性:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">2</span>,</span><br><span class="line"> foo: foo</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">setTimeout(obj.foo.bind(obj), <span class="number">300</span>);</span><br></pre></td></tr></table></figure><p>call 和 apply 也是类似的,通过对函数指定上下文来进行硬绑定,且硬绑定只能绑定一次。</p><blockquote><p><code>call()</code> 方法的作用和 <code>apply()</code> 方法类似,区别就是 <code>call()</code> 方法接受的是参数列表,而 <code>apply()</code> 方法接受的是一个参数数组。</p></blockquote><h2 id="new绑定"><a href="#new绑定" class="headerlink" title="new绑定"></a>new绑定</h2><p>new 关键字创建对象的过程其实也是一个绑定上下文的过程,所以使用 new 创建的对象的 this 也要格外注意。</p><blockquote><p>使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。</p><ol><li>创建(或者说构造)一个全新的对象。</li><li>这个新对象会被执行 [[ 原型 ]] 连接。</li><li>这个新对象会绑定到函数调用的 this 。</li><li>如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。</li></ol></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.a = a;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> bar = <span class="keyword">new</span> foo(<span class="number">2</span>);</span><br><span class="line"><span class="built_in">console</span>.log( bar.a ); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p>可以看到 new 行为的第三步就是进行 this 绑定,我们也可以从代码看到 new 行为的确有绑定 this 的能力。</p><h2 id="this-四种绑定方式排序"><a href="#this-四种绑定方式排序" class="headerlink" title="this 四种绑定方式排序"></a>this 四种绑定方式排序</h2><p>既然四种绑定都能够改变 this 的指向,那么这四种绑定的优先级是怎样的呢?结论是:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定</span><br></pre></td></tr></table></figure><p>虽然很少会出现多个场景绑定一个 this 的情况,但是知道下也能以防万一。</p><h1 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h1><p>关于 this 最后要说的就是 ES6 的箭头函数。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="comment">// 这里的 this 在此法上继承自 foo()</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.a);</span><br><span class="line"> }, <span class="number">100</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">2</span></span><br><span class="line">};</span><br><span class="line">foo.call(obj); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p>它完全等同于:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> self = <span class="keyword">this</span>; <span class="comment">// lexical capture of this</span></span><br><span class="line"> setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(self.a);</span><br><span class="line"> }, <span class="number">100</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">2</span></span><br><span class="line">};</span><br><span class="line">foo.call(obj); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p>关于箭头函数只要记住 <code>var self = this;</code> 就够了。</p><p>它其实是通过词法作用域保存当前 this 上下文传递给回调函数。本质上是抛弃了 this 原有的机制。</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>我们从四种常见 this 绑定方式和箭头函数这两个角度系统的学习了 this 绑定的知识点,相信之后你再也不怕 this 相关的知识点了!</p><p>本文还有很多可以改进的地方,如有任何意见和问题,欢迎留言指出。谢谢~</p><h1 id="推荐资料"><a href="#推荐资料" class="headerlink" title="推荐资料"></a>推荐资料</h1><ul><li>你不知道的 JavaScript (上册)</li><li><a href="https://juejin.im/entry/57c25064d342d3006b216070" target="_blank" rel="noopener">Know this, use this! (总结 this 的常见用法)</a></li></ul>]]></content>
<summary type="html">
<p>JavaScript 中的 this 一直是比较让人头疼,也是面试特别容易问及的问题。下面就参照这《你不知道的 JavaScript》来学习下 this 这个神奇的东西。</p>
<h1 id="this-到底指向何处"><a href="#this-到底指向何处" cla
</summary>
<category term="JavaScript 基础" scheme="https://violetjack.github.io/tags/JavaScript-%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>JavaScript 作用域详解</title>
<link href="https://violetjack.github.io/2019/01/12/scope/"/>
<id>https://violetjack.github.io/2019/01/12/scope/</id>
<published>2019-01-11T16:00:00.000Z</published>
<updated>2019-02-15T17:21:09.583Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文首发于<a href="https://zhuanlan.zhihu.com/c_1045249924572676096" target="_blank" rel="noopener">贝壳社区FE</a>专栏,欢迎关注!</p></blockquote><h1 id="一、什么是作用域"><a href="#一、什么是作用域" class="headerlink" title="一、什么是作用域"></a>一、什么是作用域</h1><h2 id="编译原理"><a href="#编译原理" class="headerlink" title="编译原理"></a>编译原理</h2><blockquote><ul><li><strong>分词/词法分析(Tokenizing/Lexing)</strong><br>这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代 码块被称为词法单元(token)。例如,考虑程序var a = 2;。这段程序通常会被分解成 为下面这些词法单元:var、a、=、2 、;。空格是否会被当作词法单元,取决于空格在 这门语言中是否具有意义。</li><li><strong>解析/语法分析(Parsing)</strong><br>这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法 结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST)。<br>var a = 2; 的抽象语法树中可能会有一个叫作 VariableDeclaration 的顶级节点,接下 来是一个叫作 Identifier(它的值是 a)的子节点,以及一个叫作 AssignmentExpression 的子节点。AssignmentExpression 节点有一个叫作 NumericLiteral(它的值是 2)的子 节点。</li><li><strong>代码生成</strong><br>将 AST 转换为可执行代码的过程称被称为代码生成。这个过程与语言、目标平台等息 息相关。<br>抛开具体细节,简单来说就是有某种方法可以将 var a = 2; 的 AST 转化为一组机器指 令,用来创建一个叫作 a 的变量(包括分配内存等),并将一个值储存在 a 中。</li></ul></blockquote><p>简而言之:</p><ol><li>将代码以词为单位拆分成一个个词法单元。</li><li>解析词法单元转换成 AST 语法树。</li><li>生成机器指令。</li></ol><p><img src="https://upload-images.jianshu.io/upload_images/1987062-5b4473c428c6d144.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="编译过程"></p><h2 id="编译过程"><a href="#编译过程" class="headerlink" title="编译过程"></a>编译过程</h2><p>整个编译过程有三个角色需要登场:</p><ul><li><strong>引擎</strong> 负责整个 JavaScript 程序的编译及执行过程。</li><li><strong>编译器</strong> 负责语法分析及既期代码生成。</li><li><strong>作用域</strong> 负责收集并维护所有声明的变量组成的一系列查询。</li></ul><p>那么整个 <code>var a = 2;</code> 的编译过程如下:</p><ul><li><strong>编译器</strong>拿到 <code>var a = 2;</code> 这段代码,进行语法分析。</li><li><strong>编译器</strong>分析到 <code>var a</code>,向<strong>作用域</strong>进行变量定义操作。<ul><li>如果<strong>作用域</strong>中已有 a 变量,直接通知<strong>编译器</strong>。</li><li>如果<strong>作用域中</strong>没有 a 变量,创建 a 变量并通知<strong>编译器</strong>。</li></ul></li><li><strong>编译器</strong>收到通知,继续执行并将 <code>a = 2</code> 这段代码编译为机器语言传给<strong>引擎</strong>。</li><li><strong>引擎</strong>拿到 <code>a = 2</code> 向<strong>作用域</strong>中去查找 a 变量,准备赋值操作。<ul><li>如果 a 所在作用域下有 a 变量,<strong>作用域</strong>直接通知<strong>引擎</strong>。</li><li>如果 a 所在作用域下没有 a 变量,则不断向外部作用域查找 a 变量。<ul><li>在外部作用域找到 a 变量,<strong>作用域</strong>通知<strong>引擎</strong>。</li><li>在外部作用域找 a 变量直到全局作用域下也没有找到,<strong>作用域</strong>通知<strong>引擎</strong>未找到 a 变量。</li></ul></li></ul></li><li><strong>引擎</strong>收到通知<ul><li>如果找到 a 变量,<strong>引擎</strong>在<strong>作用域</strong>内对变量 a 赋值。</li><li>如果没有找到 a 变量,<strong>引擎</strong>发出 <code>Refence Error</code> 错误。</li></ul></li></ul><p><img src="https://upload-images.jianshu.io/upload_images/1987062-22a485401f80754d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="流程图"></p><h2 id="作用域的好处"><a href="#作用域的好处" class="headerlink" title="作用域的好处"></a>作用域的好处</h2><ul><li>安全性 —— 变量和函数可以定义在最小作用域下。</li><li>减少命名冲突 —— 作用域帮我们较少命名冲突发生的概率。</li><li>代码复用性 —— 好的局部作用域可以提升代码的复用性。</li></ul><h1 id="二、LHS-与-RHS"><a href="#二、LHS-与-RHS" class="headerlink" title="二、LHS 与 RHS"></a>二、LHS 与 RHS</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>我对于 LHS 和 RHS 的理解是:所有赋值操作都是 LHS,如 <code>a = 2;</code>;而所有的取值操作都是 RHS,如 <code>console.log(a);</code>。</p><blockquote><p>当变量出现在赋值操作的左侧时进行 LHS 查询,出现在右侧时进行 RHS 查询。 —— 《你不知道的 JavaScript》</p></blockquote><h2 id="差异"><a href="#差异" class="headerlink" title="差异"></a>差异</h2><p>在非严格模式下,当变量 a 未被定义,像 <code>console.log(a)</code> 这样的RHS 查找会报 <code>ReferenceError</code> 的错误,而像 <code>b = 2</code> 这样的 LHS 查找会在全局作用域下创建变量并进行赋值。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// type: RHS, output: ReferenceError</span></span><br><span class="line">b = <span class="number">2</span>; <span class="comment">// type: LHS, output: 2</span></span><br></pre></td></tr></table></figure><p>而在严格模式下,LHS 和 RHS 的效果是相同的,都会报 <code>ReferenceError</code>。</p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a</span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a);</span><br><span class="line">}</span><br><span class="line">foo(<span class="number">2</span>);</span><br></pre></td></tr></table></figure><p>在以上例子中有 3 次 RHS 和 1 次 LHS</p><ul><li>RHS <code>foo(2)</code> 查找 foo 函数。</li><li>LHS <code>foo(2)</code> 隐藏着 <code>a = 2</code> 赋值行为。</li><li>RHS <code>console.log(a)</code> 查找 console 对象</li><li>RHS <code>console.log(a)</code> 查找 a 变量</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> b = a;</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> c = foo(<span class="number">2</span>);</span><br></pre></td></tr></table></figure><p>找出 3 次 LHS 4 次 RHS。</p><ul><li>RHS: <code>foo(2)</code> 查找 foo 函数。</li><li>LHS: <code>foo(2)</code> 隐藏有 <code>a = 2</code> 赋值行为。</li><li>LHS: <code>var c = foo(2)</code> 是赋值行为。</li><li>RHS: <code>var b = a</code> 查找 a 变量。</li><li>LHS: <code>var b = a</code> 是赋值行为。</li><li>RHS: <code>return a + b</code> 查找 a 变量。</li><li>RHS: <code>return a + b</code> 查找 b 变量。</li></ul><h1 id="三、词法作用域及欺骗词法"><a href="#三、词法作用域及欺骗词法" class="headerlink" title="三、词法作用域及欺骗词法"></a>三、词法作用域及欺骗词法</h1><h2 id="词法作用域"><a href="#词法作用域" class="headerlink" title="词法作用域"></a>词法作用域</h2><p>词法作用域就是指我们代码词法所表示的作用域。看下如下代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params">a</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> b = a * <span class="number">2</span>;</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params">c</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log( a, b, c );</span><br><span class="line"> }</span><br><span class="line"> bar( b * <span class="number">3</span> ); </span><br><span class="line">}</span><br><span class="line">foo( <span class="number">2</span> ); <span class="comment">// 2, 4, 12</span></span><br></pre></td></tr></table></figure><p>这段代码的词法作用域如图:</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-1641a475f9ce9427.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="词法作用域"></p><p>其实就是我们在代码编写时所定义的作用域即词法作用域。</p><h2 id="欺骗词法"><a href="#欺骗词法" class="headerlink" title="欺骗词法"></a>欺骗词法</h2><p>当然也有不按词法规则来的写法,称为欺骗词法。</p><h2 id="eval"><a href="#eval" class="headerlink" title="eval"></a>eval</h2><p>类似于 <code>eval()</code> 方法会将字符串解析成 JS 语言的执行。它将破坏词法作用域的规则。如</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">eval</span>(<span class="string">'var a = 3'</span>)</span><br><span class="line"> <span class="built_in">console</span>.log(a) <span class="comment">// 3</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">foo();</span><br></pre></td></tr></table></figure><h2 id="with"><a href="#with" class="headerlink" title="with"></a>with</h2><p>with 这个冷门的关键词通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">1</span>,</span><br><span class="line"> b: <span class="number">2</span>,</span><br><span class="line"> c: <span class="number">3</span></span><br><span class="line">};</span><br><span class="line"><span class="comment">// 单调乏味的重复 "obj" </span></span><br><span class="line">obj.a = <span class="number">2</span>;</span><br><span class="line">obj.b = <span class="number">3</span>;</span><br><span class="line">obj.c = <span class="number">4</span>;</span><br><span class="line"><span class="comment">// 简单的快捷方式 </span></span><br><span class="line"><span class="keyword">with</span> (obj) {</span><br><span class="line"> a = <span class="number">3</span>;</span><br><span class="line"> b = <span class="number">4</span>;</span><br><span class="line"> c = <span class="number">5</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>两种赋值方式看似等价。但如果赋值目标是 obj 对象中没有的变量,两种赋值效果是不同的。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">1</span>,</span><br><span class="line"> b: <span class="number">2</span>,</span><br><span class="line"> c: <span class="number">3</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">obj.d = <span class="number">11</span>; </span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(obj) <span class="comment">// { a: 1, b:2, c:3, d: 11 } </span></span><br><span class="line"><span class="built_in">console</span>.log(d) <span class="comment">// ReferenceError</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">1</span>,</span><br><span class="line"> b: <span class="number">2</span>,</span><br><span class="line"> c: <span class="number">3</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> (obj) {</span><br><span class="line"> d = <span class="number">11</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(obj) <span class="comment">// { a: 1, b:2, c:3 } </span></span><br><span class="line"><span class="built_in">console</span>.log(d) <span class="comment">// 11</span></span><br></pre></td></tr></table></figure><p>可以看到在 with 函数中的对于变量 d 的赋值行为(LHS)是定义在了 window 对象上的。</p><h1 id="四、函数作用域和块作用域"><a href="#四、函数作用域和块作用域" class="headerlink" title="四、函数作用域和块作用域"></a>四、函数作用域和块作用域</h1><h2 id="函数作用域"><a href="#函数作用域" class="headerlink" title="函数作用域"></a>函数作用域</h2><p>通常情况下,函数内的变量无法在函数外调用。即变量存在于函数作用域下,所以函数作用域起到了局部变量或者变量隐藏的作用。如下例子</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(a); <span class="comment">// 3</span></span><br><span class="line">}</span><br><span class="line">foo();</span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p>以上写法将 foo 方法中的 a 变量隐藏了起来。不过也产生了一个问题 —— 全局作用域下多了一个 foo 函数变量。解决这种污染的方式是立即执行函数(IIFE),我们将上面的代码进行改造:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">(<span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">3</span>;</span><br><span class="line"> <span class="built_in">console</span>.log(a); <span class="comment">// 3</span></span><br><span class="line">})();</span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// 2</span></span><br><span class="line"><span class="built_in">console</span>.log(foo) <span class="comment">// ReferenceError</span></span><br></pre></td></tr></table></figure><p>这种写法就可以将 foo 函数变量也隐藏起来,避免对全局作用域的濡染。</p><h2 id="块作用域"><a href="#块作用域" class="headerlink" title="块作用域"></a>块作用域</h2><h3 id="定义-1"><a href="#定义-1" class="headerlink" title="定义"></a>定义</h3><p>块级作用域存在于 <code>if</code>, <code>for</code>, <code>while</code>, <code>{}</code> 等语法中,这些作用域中使用 var 定义的变量是不在这个作用域内的。</p><p>块作用域和函数定义域的区别在于:函数定义域隐藏函数内的变量,而块作用域隐藏块中的变量。举个栗子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 函数作用域,隐藏变量a</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span></span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(a) <span class="comment">// ReferenceError</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 块作用域,隐藏变量 i</span></span><br><span class="line"><span class="comment">// 不隐藏变量 a (不是函数作用域)</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(i) <span class="comment">// ReferenceError</span></span><br></pre></td></tr></table></figure><h3 id="with-与-try-catch"><a href="#with-与-try-catch" class="headerlink" title="with 与 try/catch"></a>with 与 try/catch</h3><p>with 和 catch 关键字都会创建块级作用域,因为他们创建的作用域在外部作用域中无效。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> a: <span class="number">1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span>(obj) {</span><br><span class="line"> a = <span class="number">2</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(obj) <span class="comment">// { a: 2 }</span></span><br><span class="line"><span class="built_in">console</span>.log(a) <span class="comment">// ReferenceError</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="literal">undefined</span>(); <span class="comment">// 执行一个非法操作来强制制造一个异常</span></span><br><span class="line">} <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="built_in">console</span>.log(err); <span class="comment">// 能够正常执行! </span></span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(err); <span class="comment">// ReferenceError</span></span><br></pre></td></tr></table></figure><h3 id="let-与-const"><a href="#let-与-const" class="headerlink" title="let 与 const"></a>let 与 const</h3><p>let 和 const 关键字可以将变量绑定到所在的任意作用域中。换句话说,<strong>let 和 const 为其声明的变量隐式地了所在的块作用域。</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="keyword">let</span> a = <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(a) <span class="comment">// ReferenceError</span></span><br></pre></td></tr></table></figure><p>可见 const 和 let 能够保证变量隐藏在所在作用域中。</p><h3 id="var-与-let-的差异"><a href="#var-与-let-的差异" class="headerlink" title="var 与 let 的差异"></a>var 与 let 的差异</h3><p>由于 ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。</p><p>而 ES6 所提出的 let 和 const 为 JavaScript 带来了块作用域解决了这个问题。</p><p>下面列出4点 var 与 let 的差异之处:</p><ol><li>let 不存在变量提升。(var 的变量提升下文有提及)</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(foo); <span class="comment">// undefined</span></span><br><span class="line"><span class="keyword">var</span> foo = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(bar); <span class="comment">// ReferenceError</span></span><br><span class="line"><span class="keyword">let</span> bar = <span class="number">2</span>;</span><br></pre></td></tr></table></figure><ol><li>let 在块作用域内定义了变量后不受外部作用域变量影响。</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">3</span></span><br><span class="line">{</span><br><span class="line"> <span class="built_in">console</span>.log(a) <span class="comment">// ReferenceError</span></span><br><span class="line"> <span class="keyword">let</span> a</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(a) <span class="comment">// 3</span></span><br></pre></td></tr></table></figure><ol><li>不允许重复申明。</li><li>最大的不同是在于 let 作用域块作用域,而 var 只作用域函数作用域和全局作用域。</li></ol><h1 id="五、变量提升"><a href="#五、变量提升" class="headerlink" title="五、变量提升"></a>五、变量提升</h1><p>在使用 var 定义变量和使用 function 定义函数时,会出现变量提升的情况。</p><h2 id="编译顺序"><a href="#编译顺序" class="headerlink" title="编译顺序"></a>编译顺序</h2><p>看几个例子来理解下变量提升:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>; </span><br><span class="line"><span class="built_in">console</span>.log( a );</span><br><span class="line"><span class="comment">// JavaScript 的处理逻辑</span></span><br><span class="line"><span class="keyword">var</span> a;</span><br><span class="line">a = <span class="number">2</span>;</span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log( a );</span><br><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>; </span><br><span class="line"><span class="comment">// JavaScript 的处理逻辑</span></span><br><span class="line"><span class="keyword">var</span> a;</span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// undefined</span></span><br><span class="line">a = <span class="number">2</span>;</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">foo();</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a); <span class="comment">// undefined </span></span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// JavaScript 的处理逻辑</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a;</span><br><span class="line"> <span class="built_in">console</span>.log(a); <span class="comment">// undefined </span></span><br><span class="line"> a = <span class="number">2</span>;</span><br><span class="line">}</span><br><span class="line">foo();</span><br></pre></td></tr></table></figure><p><strong>为什么呢?</strong>回忆一下上文说到的编译过程就能理解了。看图!</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-22a485401f80754d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="流程图"></p><p>可以看到编译器会将变量都定义到作用域中,然后再编译代码给引擎去执行代码命令。<strong>即 <code>var a = 2;</code> 是被拆开执行的且 <code>var a</code> 变量会提前被定义。</strong></p><p>再来看一个不靠谱的函数定义方法:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">foo(); <span class="comment">// "b"</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">if</span> (a) {</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"a"</span>);</span><br><span class="line"> }</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"b"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出结果与《你不知道的 JavaScript》中的有所不同,在 node v10.5.0 中输出的是 <code>TypeError</code> 而非 <code>b</code>。这个差异有待考证。</p><h2 id="函数优先"><a href="#函数优先" class="headerlink" title="函数优先"></a>函数优先</h2><p>虽然函数和变量都会提升,但是编译器会先提升函数,再是变量。看如下例子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">foo(); <span class="comment">// 1</span></span><br><span class="line"><span class="keyword">var</span> foo;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line">foo = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>同时是函数定义,但是第二种是定义变量的形式,所以遵从函数优先原则,以上代码会变为:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> foo; <span class="comment">// 无意义</span></span><br><span class="line"></span><br><span class="line">foo(); <span class="comment">// 1</span></span><br><span class="line"></span><br><span class="line">foo = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h1 id="六、闭包"><a href="#六、闭包" class="headerlink" title="六、闭包"></a>六、闭包</h1><p>下面是人见人怕的闭包。</p><h2 id="定义-2"><a href="#定义-2" class="headerlink" title="定义"></a>定义</h2><p>当<strong>函数</strong>可以<strong>记住并访问所在的词法作用域</strong>时,就产生了闭包。<br>当<strong>函数</strong>可以<strong>记住并访问所在的词法作用域</strong>时,就产生了闭包。<br>当<strong>函数</strong>可以<strong>记住并访问所在的词法作用域</strong>时,就产生了闭包。<br>重要的定义说三遍!</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> bar;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> baz = foo();</span><br><span class="line"><span class="built_in">console</span>.log(baz()); <span class="comment">// 2 <-- 这就是闭包</span></span><br></pre></td></tr></table></figure><p>按照我们对于函数作用域的理解,函数作用域外是无法获取函数作用域内的变量的。</p><p>但是通过闭包,函数作用域被持久保存,并且闭包函数可以访问到作用域下的变量。</p><p>下面再展示几个闭包便于理解:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> fn;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">baz</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a);</span><br><span class="line"> }</span><br><span class="line"> fn = baz; <span class="comment">// 将 baz 分配给全局变量 </span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params"></span>) </span>{</span><br><span class="line"> fn(); <span class="comment">// <-- 闭包!</span></span><br><span class="line">}</span><br><span class="line">foo();</span><br><span class="line">bar(); <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">baz</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a); <span class="comment">// 2</span></span><br><span class="line"> }</span><br><span class="line"> bar(baz);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bar</span>(<span class="params">fn</span>) </span>{</span><br><span class="line"> fn(); <span class="comment">// <-- 闭包!</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">wait</span>(<span class="params">message</span>) </span>{</span><br><span class="line"> setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(message);</span><br><span class="line"> }, <span class="number">1000</span>);</span><br><span class="line">}</span><br><span class="line">wait(<span class="string">"Hello, closure!"</span>);</span><br><span class="line"><span class="comment">// timer 持有 wait 函数作用域,所以是闭包。</span></span><br></pre></td></tr></table></figure><p>上面几个例子可以归纳下闭包的特性:</p><ol><li>闭包必定是函数。</li><li>函数可以在当前词法作用域外持有并访问词法作用域。</li></ol><p>就这么简单!按照这个定义其实所有的回调函数都属于是闭包。</p><h2 id="经典的循环面试题解析"><a href="#经典的循环面试题解析" class="headerlink" title="经典的循环面试题解析"></a>经典的循环面试题解析</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= <span class="number">5</span>; i++) {</span><br><span class="line"> setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(i);</span><br><span class="line"> }, i * <span class="number">1000</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>看看以上写法最终输出的是什么呢?由于 var i = 0 是在全局作用域下,且没有任何地方存 i 的变化值,所以最终输出是 5 个 <code>6</code>。</p><p>解决方案有两种:</p><ol><li>使用闭包的持有作用域特性,为每一个 timer 函数封闭一个作用域保存当前的 i。</li><li>使用 let 块作用域封闭 for 循环中的作用域,保存当前的 i 值。</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 闭包写法</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= <span class="number">5</span>; i++) {</span><br><span class="line"> (<span class="function"><span class="keyword">function</span> (<span class="params">j</span>) </span>{</span><br><span class="line"> setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(j);</span><br><span class="line"> }, j * <span class="number">1000</span>);</span><br><span class="line"> })(i);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 块作用域写法</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i <= <span class="number">5</span>; i++) {</span><br><span class="line"> setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timer</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(i);</span><br><span class="line"> }, i * <span class="number">1000</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h1><ul><li>《你不知道的 JavaScript(上)》</li><li><a href="http://es6.ruanyifeng.com/#README" target="_blank" rel="noopener">ECMAScript 6 入门</a></li><li><a href="https://codeburst.io/javascript-learn-understand-scope-f53d6592c726" target="_blank" rel="noopener">JavaScript: Learn & Understand Scope</a></li><li><a href="https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/" target="_blank" rel="noopener">Everything you wanted to know about JavaScript scope</a></li></ul><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>本文旨在更方便和全面的理解作用域的相关知识,希望能对你有所帮助<br>JavaScript 的作用域知识不管是在面试中还是在实际工作中都是非常重要的。</p>]]></content>
<summary type="html">
<blockquote>
<p>本文首发于<a href="https://zhuanlan.zhihu.com/c_1045249924572676096" target="_blank" rel="noopener">贝壳社区FE</a>专栏,欢迎关注!</p>
</bloc
</summary>
<category term="JavaScript 基础" scheme="https://violetjack.github.io/tags/JavaScript-%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>我的 2018 年终总结</title>
<link href="https://violetjack.github.io/2018/12/31/2018-sum/"/>
<id>https://violetjack.github.io/2018/12/31/2018-sum/</id>
<published>2018-12-30T16:00:00.000Z</published>
<updated>2019-02-15T17:21:09.582Z</updated>
<content type="html"><![CDATA[<blockquote><p>以下总结仅限于个人认知,如有错误还请指正!</p></blockquote><h1 id="年度关键词"><a href="#年度关键词" class="headerlink" title="年度关键词"></a>年度关键词</h1><p><strong>2018 年是我快速提升认知、清晰认识自我的一年。</strong></p><p>在工作上,我对工作有了新的理解。</p><ol><li>理解了工作所处的系统是如何运作的;</li><li>理解了工作价值的本质;</li><li>理解了技术工作者能力提升的本质。</li></ol><p>在生活上,我确定了追寻幸福生活的人生目标。</p><ol><li>开始追寻生活的意义感;</li><li>用心体会和珍惜周围人际关系的情谊;</li><li>践行极简主义为自己减负。</li></ol><p>在个人成长上,我对于事物的思维方式和做事方法有了很大的更新。</p><ol><li>刻意锻炼自己开放性思维和独立思考的能力;</li><li>逐渐体会到知行合一的重要性;</li><li>践行以终为始地聪明学习方式。</li></ol><h1 id="对工作的新理解"><a href="#对工作的新理解" class="headerlink" title="对工作的新理解"></a>对工作的新理解</h1><h2 id="工作系统的运作方式"><a href="#工作系统的运作方式" class="headerlink" title="工作系统的运作方式"></a>工作系统的运作方式</h2><h3 id="团队"><a href="#团队" class="headerlink" title="团队"></a>团队</h3><p>我们工作的团队分工明确、各司其职,是一个非常棒的团队。在这段时间的接触和学习之后我大致理解了一下我们团队的运作方式。</p><p>从生产方式角度来说:<br>后端使用的是 Java 语言和 Sprint Boot 框架进行开发,使用 MySQL 来存储数据的。后端同学通过 Restful API 的形式与前端通信数据,并将接口文档写在公司 wiki 上便于联调。虽然技术上用的是框架,但是我发现后端同学对于业务的理解、逻辑的处理、大量数据的处理上面是非常复杂的。</p><p>测试同学我了解的更多一些。测试妹子们需要充分理解产品业务需求,写出非常完整严密的测试用例,使用代理抓包来监测各个端的网络通信情况;精通 shell 脚本并使用 Jenkins 来管理前后端项目的发布,或者使用命令行操作服务器;操作 MySQL 对数据库进行测试;写 Python、JavaScript 来进行一些自动化测试。还需要辛苦地人肉测试大量的数据;需要严密的测试思维。所以说测试妹子并不是只会对着手机和网页点点点这么简单的~</p><p>对于咱们前端,技术栈上使用了 Vue 和 React 两种,在框架上使用北京贝壳开发的 redskull2 脚手架来生成项目。项目包含前端项目和一个基于 koa 的中间 node 层项目。前端项目负责页面展示、数据通信等正常前端工作;node 层负责数据转发、单点登录验证等(其实可以做更多)。当开发完成后,我们会将前端项目打包成 JS Bundle 静态资源发布到 CDN 上,并将 node 项目部署到带有域名的服务器上。访问方式就是访问 node 端路由,由 node 端获取静态前端项目进行渲染。当然咱们也承接各种小程序的开发~这个流程大家都一样。</p><p>对于我们老大,我能看到的他主要负责团队文化建设、项目资源支持、战略方向制定这三件事。他那种看不见具体行动却时刻影响着整个团队进程的那种细雨润无声的能力真的非常厉害。</p><h3 id="公司"><a href="#公司" class="headerlink" title="公司"></a>公司</h3><p>在公司的理解上,我学到了公司的三个层级、八大部门理论。<br>三个层级分别是:执行、管理和决策。<br>八大部门分别是:服务、产品、研发、财务、人力、行政、销售、市场</p><h2 id="是什么为工作带来价值?"><a href="#是什么为工作带来价值?" class="headerlink" title="是什么为工作带来价值?"></a>是什么为工作带来价值?</h2><h3 id="万物皆产品,要用工匠精神打磨好产品"><a href="#万物皆产品,要用工匠精神打磨好产品" class="headerlink" title="万物皆产品,要用工匠精神打磨好产品"></a>万物皆产品,要用工匠精神打磨好产品</h3><p>对于我之前的努力,无论是之前的开发工作还是技术写作,其实都缺少一种产品意识。</p><p>在了解了一些产品知识之后,突然发现其实很多东西都可以看作是一款产品。无论是生活用品、各种软件、个人品牌,甚至是这篇文章其实都是一款产品。</p><p>而产品其实会带来一个价值问题,即<strong>你的产品能创造多少价值?目标群体用户是谁?传播方式有哪些?</strong>而想在职业发展和个人影响力上有大的提升,一款出色的代表作是必不可少的。如乔布斯和苹果、傅盛和360、微信和张小龙、ES6和阮一峰。</p><p>所以,万物皆产品。想要快速成长,就要用工匠精神打磨好每一款产品。</p><h3 id="产品价值是什么?"><a href="#产品价值是什么?" class="headerlink" title="产品价值是什么?"></a>产品价值是什么?</h3><p>我听过一些关于产品的看法:</p><ul><li>产品的诞生主要就是帮人偷懒;</li><li>产品必须要针对目标用户群体提供价值;</li><li>产品只有能够给用户省时间、省钱和更好的体验才是有价值的产品;</li><li>我自己私自加了一条:产品在能够利用现有资源解决痛点、满足需求时它就是有价值的。</li></ul><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><h3 id="做时间的朋友"><a href="#做时间的朋友" class="headerlink" title="做时间的朋友"></a>做时间的朋友</h3><p>先讲个反面例子,我之前虽然自诩是技术爱好者,但是我所花在技术上的时间大多都是了解新技术、追寻热门技术。虽然花了很多时间学习,但却用处很小,可能学了10个技术,5个快速过时,3个不常用,1个非常重要但是精力不够了~</p><p>而像《CSS世界》的作者张鑫旭,坚持几年只专注于 CSS 这么一门看似简单枯燥的技术。看似很傻很浪费时间,但是他的坚持却让他变成了 CSS 方面的专家。事实也证明,虽然前端技术日新月异,但是其实底层的前端使用的还是 HTML、JavaScript 和 CSS,他通过坚守 CSS 提升了影响力和基础能力。无论前端技术再变,他对于 CSS 的精通都有有用的。</p><p>所以说,对于日新月异的技术,应该要学习能够长久存在的底层技术,做时间的朋友。</p><h3 id="技术认知四阶段"><a href="#技术认知四阶段" class="headerlink" title="技术认知四阶段"></a>技术认知四阶段</h3><p>下面是我最近思考如何学习更加长久存在的技术时偶得的,我认为技术层次分为四个层次:</p><ul><li>简单使用,能够使用常用技术 API 解决业务问题,需要大量翻阅技术文档。</li><li>精通用法,对技术的各种 API 了如指掌,工作效率显著提升。</li><li>基本原理,能清晰使用流程图画出整个技术结构、运行流程和设计思想,能快速定位底层问题,并查找解决。</li><li>融会贯通,熟悉技术到源码级别。能够直接出手解决底层问题,也能创造新的工具和解决方案。</li></ul><p>个人感觉优秀的程序员应该要努力将自己的层次到底第三阶段。</p><h3 id="学习技术的本质"><a href="#学习技术的本质" class="headerlink" title="学习技术的本质"></a>学习技术的本质</h3><p>既然说了学习技术要学长久存在的,学习技术要学到能够理解基本逻辑画出运行流程和设计思路。其实就是在说我们要学习一些底层的、本质的技术。</p><p>就拿我熟悉的前端做比方,前端可以学习的框架非常多,而工作中也经常会用到不同的工具,如果说我每个工具都学习到简单使用程度,那么每个工具都需要花时间去学习、查阅资料和实践。</p><p>但其实,很多的工具和框架都是有共性的。对于每一个使用的工具深刻的理解和研究是有价值的,因为这些逻辑和思想是可迁移的。</p><p>所以说,为什么技术大牛学习技术特别快?不是他们脑子有多么聪明,只是他们经历的多了,理解了技术的本质逻辑和思想,所以可以通过对已有知识的类比快速理解一项新的技术。</p><p>学习本质的技术,提炼逻辑和思想,学会类比和复用。</p><h1 id="如何追寻幸福人生"><a href="#如何追寻幸福人生" class="headerlink" title="如何追寻幸福人生"></a>如何追寻幸福人生</h1><p>今年看了几本关于幸福的书《活出生命的意义》、《你要如何衡量你的人生》、《象与骑象人》、《向死而生》、《极简主义》、《断舍离》,这让我对生活的意义感和幸福感更加的看重。</p><h2 id="做有意义的事"><a href="#做有意义的事" class="headerlink" title="做有意义的事"></a>做有意义的事</h2><p>不论是《活出生命的意义》还是《肖生克的救赎》里都提到了一点,<strong>能够在艰难困苦中生存下去的人必然会给自己找一些有意义的事情去坚持。</strong>像前面两个例子里的主人公使用写作和雕刻来保持生活的意义感。</p><p>心理学上有一种病叫做神经性官能症,就是指在内心的空虚感和对现实世界的虚无感的煎熬下,导致患上像抑郁这类疾病。而治疗神经性官能症的最佳方案就是快速找到一些有意义的事情,哪怕只是做个手工活、参加公一项益活动、从事一项带来幸福感的工作等。</p><p>像我其实对自己的现状感觉挺幸福的,工作内容就是我喜欢做的事情,常常能在工作中产生心流。而工作内容也能够被周围环境所认可。在家里玩游戏和写作是我喜欢干的事情,写作能带给我充实感和意义感,而游戏这个不好说,但起码它也能给我带来心流。所以总体来说我还是很充实的。</p><p>萧伯纳说过:很多人过得不快乐,因为他们有太多时间去想自己是不是幸福。</p><p>所以幸福其实很简单,<strong>找到几件些让自己感觉充实和幸福的事情,并立即行动起来。</strong></p><h2 id="珍惜身边的关系"><a href="#珍惜身边的关系" class="headerlink" title="珍惜身边的关系"></a>珍惜身边的关系</h2><p>在李开复老师的《向死而生》中,当他知道自己身患重病时,才发现自己最求的不断扩大影响力来改变世界都是五彩泡沫,他在查出患了癌症之后在微博上感叹:癌症面前,人人平等。不管是有钱有权有名,最终都无足轻重。那一刻他唯一想到的就是陪伴家人、见见挚友。</p><p>自此以后,他也不再以改变世界为目标、效率至上。他变得慢下来,花更多时间陪伴老母亲、妻子和两个女儿,拜访好友。甚至会与自己相遇的每一个有缘人交谈。</p><p>年末的时候看了一部叫《幸福一家人》的电视剧,也让我非常感动。它的主题曲中有这么一句:</p><blockquote><p>曾以为,理想才是唯一。我忙着去追寻,忘了身后风景。</p></blockquote><p>所以,我一直要警示自己:<strong>为了梦想而努力的同时,一定也要珍惜身边的人,慢下来用心的陪伴。</strong>其实良好的人际关系也是幸福的关键因素。</p><h2 id="极简生活"><a href="#极简生活" class="headerlink" title="极简生活"></a>极简生活</h2><p>另外一个对生活的感悟就是做减法。</p><p>西方谚语说:“最富有的人不是拥有最多,而是需求最少。”吴军老师在他的《谷歌方法论》中也不断强调做减法的重要性。我们老大也一再提醒我要做减法。今年我也看了《断舍离》和《极简》这两本书。</p><p>但人总是有一种想拥有更多的本能欲望和一种道理都懂但不被生活教训一下就不能理解的天性。今年,自己就胸怀壮志的买了大量的书、定了宏大的目标、买了好几个专栏、想学习各种技术,但结果是什么呢?</p><p>结果就是将自己累个半死;承担着巨大的心理负担和压力;想学的太多就必然学不深。</p><p>我们老大有句话非常有哲理:“多就是少,少就是多。手里想抓的沙子越多流失的越快。”</p><p>我现在对于看书和学习的态度有很大变化,只有能够解决当下问题的知识才去学习并立即进行实践。那些可能会用到的很有用的知识其实都是妄念。就像软件开发,实现一个功能可以选的技术类型和方案有各式各样,但是最终让产品产生价值的必然只是当前在使用的技术。程序员并不需要将所有可用技术全部精通才能做出好产品来。</p><p>所以要不断给工作、生活和物品都做减法。减少大量妄念所带来的内耗和压力,专注的去做真正更有价值的事情。</p><h1 id="思维方式和做事方法的探寻"><a href="#思维方式和做事方法的探寻" class="headerlink" title="思维方式和做事方法的探寻"></a>思维方式和做事方法的探寻</h1><h2 id="思维开放,独立思考"><a href="#思维开放,独立思考" class="headerlink" title="思维开放,独立思考"></a>思维开放,独立思考</h2><h3 id="理解现实规律"><a href="#理解现实规律" class="headerlink" title="理解现实规律"></a>理解现实规律</h3><p>这是从《原则》一书中学到的:我们无法预测未来,我们能做的就是观察这个世界,理解现实世界的运行规律和原理,去总结出一条条原则,并依照原则行事。</p><h3 id="保持开放和谦逊的头脑"><a href="#保持开放和谦逊的头脑" class="headerlink" title="保持开放和谦逊的头脑"></a>保持开放和谦逊的头脑</h3><p>人总是有两个自我的 —— 情绪自我和理性自我。情绪自我会在面对冲突时激发战斗和逃避的本能,且情绪自我的力量远大于理性自我。所以面对外界各种不同信息的涌入,必须要让自己保持开放的头脑和谦逊的态度去倾听,在感受到情绪来临时不要妄下结论。</p><p>不要被情绪抑制理性的思考,训练自己的拥有开放和谦逊心态。这里也推荐一种控制情绪的方法 —— 正念冥想。亲测简单可行见效快~</p><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><h3 id="积极学习认知"><a href="#积极学习认知" class="headerlink" title="积极学习认知"></a>积极学习认知</h3><p>2018 年我看了 25 本书,听了 2 个得到专栏和 100 多篇书评,最大的收获就是在认知方面的提升。看到了很多之前从未想过的事情,也给自己带来了巨大的正能量。</p><p>所以,2019 年我还是要坚持看书,提升认知和眼界。看书真的是非常好的自我提升的方式,有人提过这么一个逻辑:<strong>从古至今,牛人没有几个是不看书的。</strong>逻辑虽不严密,但大体上没毛病~</p><h3 id="用行动理解认知"><a href="#用行动理解认知" class="headerlink" title="用行动理解认知"></a>用行动理解认知</h3><p>在看了大量的书之后,自己犯了两个错误。一是感受到认知的提升后感觉自己与别人之间有不同,产生了优越感。二是认知提升之后,心变得很大。感觉自己什么都懂,自己能够快速成为成功人士。但这一切都是错觉。</p><p>知道了前人的经验和知识其实并没有站在巨人的肩膀上。打个不太恰当的比方来说<strong>这只是看到了巨人的肩膀</strong>,真正要站在巨人的肩膀上还是得靠自己脚踏实地走上巨人的肩膀才行。</p><p>在实际的践行中,才能更好地理解和领悟到所学知识的一些要点和难点,反过来提升和纠正认知,再采取下一步行动。这种行知、知行的反复才是真正提升认知和能力的途径。</p><h3 id="知到深处便是行;行到极致便是知。"><a href="#知到深处便是行;行到极致便是知。" class="headerlink" title="知到深处便是行;行到极致便是知。"></a>知到深处便是行;行到极致便是知。</h3><p>我逐渐开始理解我们老大为何如此推崇王阳明的知行合一,因为这就是他知行合一之后的感悟和认知。</p><p>所以,其他的不说了,多多看书,积极思考,踏实践行吧!</p><h2 id="聪明的学习方式"><a href="#聪明的学习方式" class="headerlink" title="聪明的学习方式"></a>聪明的学习方式</h2><p>今年在学习方式上也收获了很多。</p><h3 id="整体学习"><a href="#整体学习" class="headerlink" title="整体学习"></a>整体学习</h3><p>整体学习法是一本叫《如何高效学习》的书中学到的。作者史蒂芬杨在10天内搞定了线性代数,一年内搞定思念大学课程。这种学习达人的秘籍就是整体学习法。</p><p>他的方法我倒还没有机会践行,但是给我的启发已经开始起作用了。就是在学习技术的时候,不要上来就死扣文档,整体理解技术的实现方式和设计思想,通过画图的方式将这项技术整体勾勒出来。在理解了技术整体之后,学习其中的细节和用法会变得更加顺畅。</p><h3 id="策略学习"><a href="#策略学习" class="headerlink" title="策略学习"></a>策略学习</h3><p>策略学习这个东西在很多书中都有提到过。即我们看书和学习不要从头到尾开始看,必须带着目的去找到能解决当下问题和需求的知识看。</p><p>这是一种反常规的学习方式,一开始非常不习惯,总觉得自己缺了些什么。但这种以终为始的看书和学习方式,真的可以大大提高学习的效率。</p><p>我就在一周内看完了《JavaScript 高级程序设计》,用的就是策略学习的方法。核心是重点知识细看、次要知识可查。整体学习理解,明确学习重点和学习目标。</p><h3 id="深度学习"><a href="#深度学习" class="headerlink" title="深度学习"></a>深度学习</h3><p>当然,还是有一些高知识密度的干货是我们必须拿下的,这时候就可以使用深度学习的方式来进行攻坚战。</p><p>当然,深度学习也可以用到之前说的整体学习法来提高效率和理解。最近我很喜欢用画流程图和结构图的学习方式。</p><h1 id="今年简史"><a href="#今年简史" class="headerlink" title="今年简史"></a>今年简史</h1><p>说了一堆认知,说下今年的历程(篇幅较长)。</p><p>17年10月,我宝贝女儿的出生触发了我生活轨迹的改变,多米诺骨牌由此推动。</p><p>在孩子出生后,发现自己很难有时间大段大段的时间看书,于是买了喜马拉雅的会员听书。</p><p>在喜马拉雅最爱听《天天听好书》的一个栏目,它天天会推送一些好书。听了那么多书,多很多成长类书籍产生了兴趣。</p><p>借阅和购买了大量书籍阅读,像《富爸爸穷爸爸》、《财务自由之路》、《跃迁》、《刻意练习》、《关键对话》等。这些书籍给我打开了一个完全不同的世界。之前我看到的世界就是家庭生活、工作以及一些技术论坛,但是通过阅读我发现了很多人生的可能性,开始产生了对于现状的强烈不满感。</p><p>2018年4月,我毅然决定从安逸的小公司开发工作上裸辞。在家修整两月自我沉淀、安心带娃。在这期间我坚持每天5点起床,花4-5个小时更新技术博客。</p><p>2018年5月,我意外收到了饿了么的一位前辈的邀请去参见面试。当时饿了么对于我而言是前端工程师的圣地,是我理想中打开我新世界的最佳选择。在匆忙准备之后我去饿了么总部和面试官聊了半小时,但由于我前端基础不过关遗憾失败。</p><p>故事当然没那么快结束。由于当时我真的非常想加入一家优秀的互联网公司打开我对新世界的门窗,我继续和那位前辈沟通,请教我的技术缺陷和提升方式,我也一直很感激他给我的一系列建议。所以我在一个月的时间里学习完了《JavaScript 高级程序设计》、《算法》并且将150道 LeetCode 算法题刷了3遍。当时基本情况是早上起来就学习,一直到晚上七八点。</p><p>努力了一个月后,那位前辈给了我一次再去饿了么面试的机会。我坚定的告诉自己这次一定要成功。但故事并没那么顺利~由于当时饿了么工位不多,所以HR认为我并不胜任仅有的几个工位。虽然再次失败,但是我还是由衷的感谢那位前辈给我这么大的帮助。</p><p>由于刚好同学结婚,所以修整了一个星期。回到上海重整旗鼓开始投简历刚过一天。那位前辈又给了我一次面试上海链家的机会。这次机会我终于把握住了,6月份成功入职现在的公司。</p><p>在链家的6个月,让我成长了非常多。了解了互联网公司和业务团队的工作方式、看到了那么多优秀的同事、提升了大量的认知、学习到了很多做事的经验,这其中很多内容都整理在上文中了。这里也非常感谢军哥、彪哥和晖姐的帮助。我从一个小白逐渐的开始适应了互联网公司的工作方式和节奏。</p><p>就在18年的最后一个月,我又折腾了一下自己。报名参加了古典老师的个人战略课。白天忙着上班,晚上学习课程、参加小组讨论、周末还要按时交作业,把我给累的够呛。所幸这些折腾很有价值,古典老师的课程质量很高,我也遇到了非常帮的助教和同组小伙伴。我们在相互激励下完成了为期一个月的课程,在小组讨论中总能擦除很多火花,收获非常多。具体的收获我会在之后的笔记中整理出来,主要是理解工作的价值和定位,做好自己的职业生涯规划。</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>2018 是我认知提升巨大的一年,带动着的是我生活现状的巨大改变。2019 年我对自己的期望是:</p><blockquote><p>远处看系统,近处看本质。<br>学习有策略,知行要合一。<br>产品看价值,妄念做减法。<br>技术懂原理,类比可迁移。</p></blockquote>]]></content>
<summary type="html">
<blockquote>
<p>以下总结仅限于个人认知,如有错误还请指正!</p>
</blockquote>
<h1 id="年度关键词"><a href="#年度关键词" class="headerlink" title="年度关键词"></a>年度关键词</h1><p><s
</summary>
<category term="年终总结" scheme="https://violetjack.github.io/tags/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>常用数据集合 API 整理</title>
<link href="https://violetjack.github.io/2018/05/10/%E6%95%B0%E6%8D%AE%E9%9B%86%E5%90%88%E5%B8%B8%E7%94%A8%20API%20%E6%95%B4%E7%90%86/"/>
<id>https://violetjack.github.io/2018/05/10/数据集合常用 API 整理/</id>
<published>2018-05-09T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.256Z</updated>
<content type="html"><![CDATA[<blockquote><p>最近刷 LeetCode 题总是遇到 Array、Object、Set、Map 这类数据结构,虽然知道有些什么 API,但是每次用总是要查查 MDN 才放心。非常浪费时间,所以这里好好整理下这些数据集合的常用 API。</p></blockquote><h1 id="Array"><a href="#Array" class="headerlink" title="Array"></a>Array</h1><ul><li>Array.length 返回数组长度</li><li>Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。传入类似数组对象及map回调方法,返回新数组。</li><li>fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。返回修改后的数组(原数组)。</li><li>forEach() 方法对数组的每个元素执行一次提供的函数。</li><li>reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。</li><li>includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。</li><li>join() 方法将一个数组(或一个<a href="https://developer.mozilla.org/zh-CN//docs/Web/JavaScript/Guide/Indexed_collections#Working_with_array-like_objects" target="_blank" rel="noopener">类数组对象</a>)的所有元素连接成一个字符串并返回这个字符串。</li><li>keys() 方法返回一个新的Array迭代器,它包含数组中每个索引的键。</li><li>map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。</li><li>数组操作:<ul><li>concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是<strong>返回一个新数组</strong>。</li><li>pop() 方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。</li><li>push() 方法将一个或多个元素添加到数组的末尾,并返回新数组的长度。</li><li>shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。</li><li>unshift() 方法将一个或多个元素添加到数组的开头,并返回新数组的长度。</li><li>reverse() 方法将数组中元素的位置颠倒。</li><li>sort() 方法用<a href="https://en.wikipedia.org/wiki/Sorting_algorithm#Stability" target="_blank" rel="noopener">就地( in-place )的算法</a>对数组的元素进行排序,并返回数组。 sort 排序不一定是<a href="https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95#.E7.A9.A9.E5.AE.9A.E6.80.A7" target="_blank" rel="noopener">稳定的</a>。默认排序顺序是根据字符串Unicode码点。会改变原数组,返回排序后的数组。</li><li>splice() 方法通过删除现有元素和/或添加新元素来更改一个数组的内容。可以用于删除也可用于插入数据。返回被删除的元素(集合)。</li><li>slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分<strong>浅拷贝到一个新数组对象。且原始数组不会被修改。</strong>截取内容的范围是 start <= val < end。可以使用 arr.slice() 对数组进行浅拷贝。</li></ul></li></ul><h1 id="Object"><a href="#Object" class="headerlink" title="Object"></a>Object</h1><ul><li>Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的<strong>proto</strong>。 可以通过 <code>Object.create(null)</code> 创建空对象。</li><li>Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。</li><li>Object.freeze() 方法可以冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。</li><li>Object.seal() 方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要可写就可以改变。</li><li>Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for...in" title="for...in语句以任意顺序遍历一个对象的可枚举属性。对于每个不同的属性,语句都会被执行。" target="_blank" rel="noopener"><code>for...in</code></a> 循环遍历该对象时返回的顺序一致 (两者的主要区别是 一个 for-in 循环还会枚举其原型链上的属性)。</li></ul><h1 id="Set"><a href="#Set" class="headerlink" title="Set"></a>Set</h1><ul><li>size 属性将会返回<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set" title="Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。" target="_blank" rel="noopener"><code>Set</code></a>对象中元素的个数。</li><li>add() 方法用来向一个 Set 对象的末尾添加一个指定的值。</li><li>clear() 方法用来清空一个 Set 对象中的所有元素。</li><li>delete() 方法可以从一个 Set 对象中删除指定的元素。</li><li>forEach 方法根据集合中元素的顺序,对每个元素都执行提供的 callback 函数一次。</li><li>has() 方法返回一个布尔值来指示对应的值value是否存在Set对象中</li></ul><h1 id="Map"><a href="#Map" class="headerlink" title="Map"></a>Map</h1><ul><li>size 可访问属性返回 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Map" title="此页面仍未被本地化, 期待您的翻译!" target="_blank" rel="noopener"><code>Map</code></a> 对象的元素数量。</li><li>get() 方法用来获取一个 Map 对象中指定的元素。如果找不到返回 <code>undefined</code></li><li>set() 方法为 Map 对象添加一个指定键(key)和值(value)的新元素。</li><li>clear() 方法会移除Map对象中的所有元素。</li><li>delete() 方法用于移除 Map 对象中指定的元素。</li><li>forEach() ?方法将会以插入顺序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数。</li><li>has() 方法返回一个布尔值,用来表明 map 中是否存在指定元素。</li></ul><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>先就这些啦~整理出来以免每次用到的时候都去查 MDN (我都查烦了),这种常用的整理出来记在脑子里比较好。</p>]]></content>
<summary type="html">
<blockquote>
<p>最近刷 LeetCode 题总是遇到 Array、Object、Set、Map 这类数据结构,虽然知道有些什么 API,但是每次用总是要查查 MDN 才放心。非常浪费时间,所以这里好好整理下这些数据集合的常用 API。</p>
</blockquo
</summary>
</entry>
<entry>
<title>LeetCode 算法题刷题心得(JavaScript)</title>
<link href="https://violetjack.github.io/2018/05/09/LeetCode%20%E7%AE%97%E6%B3%95%E9%A2%98%E5%88%B7%E9%A2%98%E5%BF%83%E5%BE%97%EF%BC%88JavaScript%EF%BC%89/"/>
<id>https://violetjack.github.io/2018/05/09/LeetCode 算法题刷题心得(JavaScript)/</id>
<published>2018-05-08T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.252Z</updated>
<content type="html"><![CDATA[<blockquote><p>花了十几天,把《算法》看了一遍然后重新 AC 了一遍 LeetCode 的题,收获颇丰。这次好好记录下心得。<br>我把所有做题的代码都放在 github 上以供参考。<br>项目地址:<a href="https://github.com/violetjack/LeetCodeACByJS" target="_blank" rel="noopener">https://github.com/violetjack/LeetCodeACByJS</a><br>题目地址:<a href="https://leetcode.com/problemset/top-interview-questions/" target="_blank" rel="noopener">https://leetcode.com/problemset/top-interview-questions/</a></p></blockquote><p>说来惭愧,之前写的《LeetCode 逻辑题分享》其实自己动手做的比较少,都是看解决方案。更加关键的是<strong>我没有系统地去学习过算法</strong>(自学的编程)。所以导致以下几个问题:</p><ul><li>看题不懂方法论,理解他人方案困难。</li><li>解题方法通过看别人的方案去归纳,照着抄。(其实都是有系统的算法写法的)</li><li>很多题目看了答案只是知其然而不知其所以然。</li><li>很多答案(讨论区的方案)是有错误的,却把它当正确答案来发。</li></ul><p>之后,我看了《算法(第4版)》一书,重新去做并且试着去 AC 题目,问题又是一堆堆的。所以这次比第一次刷题时间要久不少。</p><h1 id="各类题的解决方案"><a href="#各类题的解决方案" class="headerlink" title="各类题的解决方案"></a>各类题的解决方案</h1><p>话不多说,系统整理下解题的一些算法和解决方案</p><h2 id="二叉树"><a href="#二叉树" class="headerlink" title="二叉树"></a>二叉树</h2><p>二叉树大多使用递归的方式左右两个元素向下递归。比如:</p><p><strong>计算二叉树最大深度</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> maxDepth = <span class="function"><span class="keyword">function</span> (<span class="params">root</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (root == <span class="literal">null</span>) <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span> + <span class="built_in">Math</span>.max(maxDepth(root.left), maxDepth(root.right))</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p><strong>将二叉树以二维数组形式表现</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> levelOrder = <span class="function"><span class="keyword">function</span>(<span class="params">root</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> ans = []</span><br><span class="line"> helper(root, ans, <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">return</span> ans</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">helper</span>(<span class="params">node, ans, i</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (node == <span class="literal">null</span>) <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">if</span> (i == ans.length) ans.push([])</span><br><span class="line"> ans[i].push(node.val)</span><br><span class="line"></span><br><span class="line"> helper(node.left, ans, i + <span class="number">1</span>)</span><br><span class="line"> helper(node.right, ans, i + <span class="number">1</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>都是通过递归方式逐层向下去查找二叉树数据。</p><h2 id="可能性问题"><a href="#可能性问题" class="headerlink" title="可能性问题"></a>可能性问题</h2><p>这类题一般是告诉你一组数据,然后求出可能性、最小值或最大值。比如:</p><p><strong>给定几种面额的硬币和一个总额,使用最少的硬币凑成这个总额。</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> coinChange = <span class="function"><span class="keyword">function</span> (<span class="params">coins, amount</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> max = amount + <span class="number">1</span></span><br><span class="line"> <span class="keyword">let</span> dp = <span class="keyword">new</span> <span class="built_in">Array</span>(amount + <span class="number">1</span>)</span><br><span class="line"> dp.fill(max)</span><br><span class="line"> dp[<span class="number">0</span>] = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i < max; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j < coins.length; j++) {</span><br><span class="line"> <span class="keyword">if</span> (coins[j] <= i) {</span><br><span class="line"> dp[i] = <span class="built_in">Math</span>.min(dp[i], dp[i - coins[j]] + <span class="number">1</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dp[amount] > amount ? <span class="number">-1</span> : dp[amount]</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>使用了动态规划(DP),将从 0 到目标额度所需的最小硬币数都列出来。</p><p><strong>求出从矩阵左上角走到右下角,且只能向右向下移动,一共有多少种可能性。</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> uniquePaths = <span class="function"><span class="keyword">function</span> (<span class="params">m, n</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> pos = <span class="keyword">new</span> <span class="built_in">Array</span>(m)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < m; i++) {</span><br><span class="line"> pos[i] = <span class="keyword">new</span> <span class="built_in">Array</span>(n)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < n; i++) {</span><br><span class="line"> pos[<span class="number">0</span>][i] = <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < m; i++) {</span><br><span class="line"> pos[i][<span class="number">0</span>] = <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i < m; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">1</span>; j < n; j++) {</span><br><span class="line"> pos[i][j] = pos[i - <span class="number">1</span>][j] + pos[i][j - <span class="number">1</span>]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> pos[m - <span class="number">1</span>][n - <span class="number">1</span>]</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>这题就是使用了动态规划逐步列出每一格的可能性,最后返回右下角的可能性。</p><p><strong>获取给定数组连续元素累加最大值</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> maxSubArray = <span class="function"><span class="keyword">function</span> (<span class="params">nums</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> count = nums[<span class="number">0</span>], maxCount = nums[<span class="number">0</span>]</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i < nums.length; i++) {</span><br><span class="line"> count = <span class="built_in">Math</span>.max(count + nums[i], nums[i])</span><br><span class="line"> maxCount = <span class="built_in">Math</span>.max(maxCount, count) </span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> maxCount</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>上面这题通过不断对比最大值来保留并返回最大值。</p><p>其实,可能性问题使用<strong>动态规划</strong>要比使用 DFS、BFS 算法更加简单而容易理解。(我使用 DFS 经常报 TLE)</p><h2 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h2><p>一般遇到的查找问题,如查找某个值一般会用到一下方法:</p><ul><li>排序算法(排序便于查找)</li><li>二分查找</li><li>索引移动查找(这个方法名自己想的,大概就这个意思~)</li></ul><p><strong>查找横向和纵向都递增的二维矩阵中的某个值</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> searchMatrix = <span class="function"><span class="keyword">function</span> (<span class="params">matrix, target</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (matrix.length == <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"> <span class="keyword">let</span> row = <span class="number">0</span>, col = matrix[<span class="number">0</span>].length - <span class="number">1</span></span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"> <span class="keyword">if</span> (matrix[row][col] > target && col > <span class="number">0</span>) {</span><br><span class="line"> col--</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (matrix[row][col] < target && row < matrix.length - <span class="number">1</span>) {</span><br><span class="line"> row++</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (matrix[row][col] == target) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>先将位置定位在右上角,通过改变位置坐标来找到目标值。使用了索引移动查找法来找到结果。</p><p><strong>找到数组中最左边和最右边的某个数字所在位置</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> searchRange = <span class="function"><span class="keyword">function</span> (<span class="params">nums, target</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> targetIndex = binarySearch(nums, target, <span class="number">0</span>, nums.length - <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">if</span> (targetIndex == <span class="number">-1</span>) <span class="keyword">return</span> [<span class="number">-1</span>, <span class="number">-1</span>]</span><br><span class="line"> <span class="keyword">let</span> l = targetIndex, r = targetIndex</span><br><span class="line"> <span class="keyword">while</span>(l > <span class="number">0</span> && nums[l - <span class="number">1</span>] == target){</span><br><span class="line"> l--</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span>(r < nums.length - <span class="number">1</span> && nums[r + <span class="number">1</span>] == target){</span><br><span class="line"> r++</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> [l, r]</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">binarySearch</span>(<span class="params">arr, val, lo, hi</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (hi < lo) <span class="keyword">return</span> <span class="number">-1</span></span><br><span class="line"> <span class="keyword">let</span> mid = lo + <span class="built_in">parseInt</span>((hi - lo) / <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (val < arr[mid]) {</span><br><span class="line"> <span class="keyword">return</span> binarySearch(arr, val, lo, mid - <span class="number">1</span>)</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (val > arr[mid]) {</span><br><span class="line"> <span class="keyword">return</span> binarySearch(arr, val, mid + <span class="number">1</span>, hi)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> mid</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>这题使用<strong>二分法</strong>来查找到某个目标数字的索引值,然后<strong>索引移动法</strong>分别向左和向右查找字符。获取左右两侧的索引值返回。</p><h2 id="回文"><a href="#回文" class="headerlink" title="回文"></a>回文</h2><p>所谓回文,就是正着读反着读是一样的。使用索引两边向中间移动的方式来判断是否为回文。</p><p><strong>找到给定字符串中某段最长的回文</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> longestPalindrome = <span class="function"><span class="keyword">function</span> (<span class="params">s</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> maxLength = <span class="number">0</span>, left = <span class="number">0</span>, right = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < s.length; i++) {</span><br><span class="line"> <span class="keyword">let</span> singleCharLength = getPalLenByCenterChar(s, i, i)</span><br><span class="line"> <span class="keyword">let</span> doubleCharLength = getPalLenByCenterChar(s, i, i + <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">let</span> max = <span class="built_in">Math</span>.max(singleCharLength, doubleCharLength)</span><br><span class="line"> <span class="keyword">if</span> (max > maxLength) {</span><br><span class="line"> maxLength = max</span><br><span class="line"> left = i - <span class="built_in">parseInt</span>((max - <span class="number">1</span>) / <span class="number">2</span>)</span><br><span class="line"> right = i + <span class="built_in">parseInt</span>(max / <span class="number">2</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> s.slice(left, right + <span class="number">1</span>)</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getPalLenByCenterChar</span>(<span class="params">s, left, right</span>) </span>{</span><br><span class="line"> <span class="comment">// 中间值为两个字符,确保两个字符相等</span></span><br><span class="line"> <span class="keyword">if</span> (s[left] != s[right]){</span><br><span class="line"> <span class="keyword">return</span> right - left</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span> (left > <span class="number">0</span> && right < s.length - <span class="number">1</span>) {</span><br><span class="line"> left--</span><br><span class="line"> right++</span><br><span class="line"> <span class="keyword">if</span> (s[left] != s[right]){</span><br><span class="line"> <span class="keyword">return</span> right - left - <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> right - left + <span class="number">1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="路径题"><a href="#路径题" class="headerlink" title="路径题"></a>路径题</h2><p>路径题可以使用深度优先(DFS)和广度优先(BFS)算法来做。我比较常用的是使用 DFS 来做。通过递归将走过的路径进行标记来不断往前找到目标路径。如:</p><p><strong>通过给定单词在二维字母数组中查找是否能使用邻近字母组成这个单词</strong>(<a href="https://leetcode.com/problems/word-search-ii/description/" target="_blank" rel="noopener">212题</a>)<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> hasWord = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> findWords = <span class="function"><span class="keyword">function</span> (<span class="params">board, words</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> ans = []</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> word <span class="keyword">of</span> words) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j < board.length; j++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < board[<span class="number">0</span>].length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (board[j][i] == word[<span class="number">0</span>]) {</span><br><span class="line"> hasWord = <span class="literal">false</span></span><br><span class="line"> DFS(word, board, <span class="number">0</span>, j, i, <span class="string">""</span>)</span><br><span class="line"> <span class="keyword">if</span> (hasWord) {</span><br><span class="line"> <span class="keyword">if</span> (!ans.includes(word))</span><br><span class="line"> ans.push(word)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">DFS</span>(<span class="params">word, board, index, j, i, subStr</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (word[index] == board[j][i]) {</span><br><span class="line"> subStr += board[j][i]</span><br><span class="line"> board[j][i] = <span class="string">"*"</span></span><br><span class="line"> <span class="keyword">if</span> (j < board.length - <span class="number">1</span>)</span><br><span class="line"> DFS(word, board, index + <span class="number">1</span>, j + <span class="number">1</span>, i, subStr)</span><br><span class="line"> <span class="keyword">if</span> (j > <span class="number">0</span>)</span><br><span class="line"> DFS(word, board, index + <span class="number">1</span>, j - <span class="number">1</span>, i, subStr)</span><br><span class="line"> <span class="keyword">if</span> (i < board[<span class="number">0</span>].length - <span class="number">1</span>)</span><br><span class="line"> DFS(word, board, index + <span class="number">1</span>, j, i + <span class="number">1</span>, subStr)</span><br><span class="line"> <span class="keyword">if</span> (i > <span class="number">0</span>)</span><br><span class="line"> DFS(word, board, index + <span class="number">1</span>, j, i - <span class="number">1</span>, subStr)</span><br><span class="line"> board[j][i] = word[index]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (index >= word.length || subStr == word) {</span><br><span class="line"> hasWord = <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>由于 DFS 是一条路走到黑,如果每个元素都去使用 DFS 来找会出现超时的情况。如果条件允许(如查找递增数组)可以通过<strong>设置缓存</strong>来优化 DFS 查找超时问题。</p><p><strong>获取二维矩阵中最大相邻递增数组长度。</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> dirs = [[<span class="number">0</span>, <span class="number">1</span>], [<span class="number">1</span>, <span class="number">0</span>], [<span class="number">0</span>, <span class="number">-1</span>], [<span class="number">-1</span>, <span class="number">0</span>]]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> longestIncreasingPath = <span class="function"><span class="keyword">function</span> (<span class="params">matrix</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (matrix.length == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"> <span class="keyword">const</span> m = matrix.length, n = matrix[<span class="number">0</span>].length</span><br><span class="line"> <span class="keyword">let</span> max = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> cache = <span class="keyword">new</span> <span class="built_in">Array</span>(m)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < m; i++){</span><br><span class="line"> <span class="keyword">let</span> child = <span class="keyword">new</span> <span class="built_in">Array</span>(n)</span><br><span class="line"> child.fill(<span class="number">0</span>)</span><br><span class="line"> cache[i] = child</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < m; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j < n; j++) {</span><br><span class="line"> <span class="keyword">let</span> len = dfs(matrix, i, j, m, n, cache)</span><br><span class="line"> max = <span class="built_in">Math</span>.max(max, len)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> max</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">dfs</span>(<span class="params">matrix, i, j, m, n, cache</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (cache[i][j] != <span class="number">0</span>) <span class="keyword">return</span> cache[i][j]</span><br><span class="line"> <span class="keyword">let</span> max = <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> dir <span class="keyword">of</span> dirs){</span><br><span class="line"> <span class="keyword">let</span> x = i + dir[<span class="number">0</span>], y = j + dir[<span class="number">1</span>]</span><br><span class="line"> <span class="keyword">if</span>(x < <span class="number">0</span> || x >= m || y < <span class="number">0</span> || y >= n || matrix[x][y] <= matrix[i][j]) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">let</span> len = <span class="number">1</span> + dfs(matrix, x, y, m, n, cache)</span><br><span class="line"> max = <span class="built_in">Math</span>.max(max, len)</span><br><span class="line"> }</span><br><span class="line"> cache[i][j] = max</span><br><span class="line"> <span class="keyword">return</span> max</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>将已使用 DFS 查找过的长度放入缓存,如果有其他元素走 DFS 走到当前值,直接返回缓存最大值即可。</p><h2 id="链表"><a href="#链表" class="headerlink" title="链表"></a>链表</h2><p>链表从 JS 的角度来说就是一串对象使用指针连接的数据结构。合理使用 <code>next</code> 指针改变指向来完成对链表的一系列操作。如:</p><p><strong>链表的排序:</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> sortList = <span class="function"><span class="keyword">function</span> (<span class="params">head</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) <span class="keyword">return</span> head</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> prev = <span class="literal">null</span>, slow = head, fast = head</span><br><span class="line"> <span class="keyword">while</span> (fast != <span class="literal">null</span> && fast.next != <span class="literal">null</span>) {</span><br><span class="line"> prev = slow</span><br><span class="line"> slow = slow.next</span><br><span class="line"> fast = fast.next.next</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> prev.next = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> l1 = sortList(head)</span><br><span class="line"> <span class="keyword">let</span> l2 = sortList(slow)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> merge(l1, l2)</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">merge</span>(<span class="params">l1, l2</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> l = <span class="keyword">new</span> ListNode(<span class="number">0</span>), p = l;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (l1 != <span class="literal">null</span> && l2 != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">if</span> (l1.val < l2.val) {</span><br><span class="line"> p.next = l1;</span><br><span class="line"> l1 = l1.next;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> p.next = l2;</span><br><span class="line"> l2 = l2.next;</span><br><span class="line"> }</span><br><span class="line"> p = p.next;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (l1 != <span class="literal">null</span>)</span><br><span class="line"> p.next = l1;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (l2 != <span class="literal">null</span>)</span><br><span class="line"> p.next = l2;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> l.next;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>使用了<strong>自上而下的归并排序方法</strong>对链表进行了排序。使用 <code>slow.next</code> 和 <code>fast.next.next</code> 两种速度获取链表节点,从而获取中间值。</p><p><strong>链表的倒序</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> reverseList = <span class="function"><span class="keyword">function</span>(<span class="params">head</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> ans = <span class="literal">null</span>,cur = head</span><br><span class="line"> <span class="keyword">while</span> (cur != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">let</span> nextTmp = cur.next</span><br><span class="line"> cur.next = ans</span><br><span class="line"> ans = cur</span><br><span class="line"> cur = nextTmp</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><h2 id="排序"><a href="#排序" class="headerlink" title="排序"></a>排序</h2><p>排序和查找算是算法中最重要的问题了。常用的排序算法有:</p><ul><li>插入排序</li><li>选择排序</li><li>快速排序</li><li>归并排序</li><li>计数排序</li></ul><p>更多排序算法的知识点可参考<a href="https://www.jianshu.com/p/1b4068ccd505" target="_blank" rel="noopener">《JS家的排序算法》</a>,文章作者图文并茂的讲解了各种排序算法,很容易理解。<br>举几个排序算法的栗子:</p><p><strong>求数组中第K大的值</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @param {number[]} nums</span></span><br><span class="line"><span class="comment"> * @param {number} k</span></span><br><span class="line"><span class="comment"> * @return {number}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> findKthLargest = <span class="function"><span class="keyword">function</span> (<span class="params">nums, k</span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i <= k; i++) {</span><br><span class="line"> <span class="keyword">let</span> max = i</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = i; j < nums.length; j++) {</span><br><span class="line"> <span class="keyword">if</span> (nums[j] > nums[max]) max = j</span><br><span class="line"> }</span><br><span class="line"> swap(nums, i, max)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> nums[k - <span class="number">1</span>]</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">swap</span>(<span class="params">arr, a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> tmp = arr[a]</span><br><span class="line"> arr[a] = arr[b]</span><br><span class="line"> arr[b] = tmp</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>使用了<strong>选择排序</strong>排列了前 K 个值得到结果。</p><p><strong>对有重复值的数组 <code>[2,0,2,1,1,0]</code> 排序</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> sortColors = <span class="function"><span class="keyword">function</span> (<span class="params">nums</span>) </span>{</span><br><span class="line"> sort(nums, <span class="number">0</span>, nums.length - <span class="number">1</span>)</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sort</span>(<span class="params">arr, lo, hi</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (hi <= lo) <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">let</span> lt = lo, i = lo + <span class="number">1</span>, gt = hi;</span><br><span class="line"> <span class="keyword">let</span> v = arr[lo]</span><br><span class="line"> <span class="keyword">while</span> (i <= gt) {</span><br><span class="line"> <span class="keyword">if</span> (arr[i] < v) swap(arr, lt++, i++)</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (arr[i] > v) swap(arr, i, gt--)</span><br><span class="line"> <span class="keyword">else</span> i++</span><br><span class="line"> }</span><br><span class="line"> sort(arr, lo, lt - <span class="number">1</span>)</span><br><span class="line"> sort(arr, gt + <span class="number">1</span>, hi)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">swap</span>(<span class="params">arr, a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> x = arr[a]</span><br><span class="line"> arr[a] = arr[b]</span><br><span class="line"> arr[b] = x</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>这种有重复值的使用<strong>三向切分的快速排序</strong>是非常好的解决方案。当然,<strong>计数排序</strong>法可是不错的选择。<br>还有之前提到的链表的排序使用的是<strong>归并排序</strong>。</p><h2 id="算术题"><a href="#算术题" class="headerlink" title="算术题"></a>算术题</h2><p>算术题看似简单,但是遇到最大的问题就是:如果使用累加、累成这种常熟级别的增长,遇到很大的数字会出现 TLE (超出时间限制)。所以,我们要用指数级别的增长来找到结果。如:</p><p><strong>计算 x 的 n 次方</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myPow = <span class="function"><span class="keyword">function</span> (<span class="params">x, n</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (n == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> (n < <span class="number">0</span>) {</span><br><span class="line"> n = -n</span><br><span class="line"> x = <span class="number">1</span> / x</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> (n % <span class="number">2</span> == <span class="number">0</span>) ? myPow(x * x, <span class="built_in">parseInt</span>(n / <span class="number">2</span>)) : x * myPow(x * x, <span class="built_in">parseInt</span>(n / <span class="number">2</span>));</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>一开始我使用了 x<em>x 这么乘上 n 次,但是遇到 n 太大就直接超时了。使用以上方案:2^9^ = 2 </em> 4^4^ = 2 <em> 8^2^ = 2 </em> 64 = 128<br>直接从常熟级变化变为指数级变化,这一点在数学运算中是需要注意的。</p><p><strong>求 x 的平方根</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> mySqrt = <span class="function"><span class="keyword">function</span> (<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> l = <span class="number">0</span>, r = x</span><br><span class="line"> <span class="keyword">while</span> (<span class="literal">true</span>) {</span><br><span class="line"> <span class="keyword">let</span> mid = <span class="built_in">parseInt</span>(l + (r - l) / <span class="number">2</span>)</span><br><span class="line"> <span class="keyword">if</span> (mid * mid > x) {</span><br><span class="line"> r = mid - <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (mid * mid < x) {</span><br><span class="line"> <span class="keyword">if</span> ((mid + <span class="number">1</span>) * (mid + <span class="number">1</span>) > x) {</span><br><span class="line"> <span class="keyword">return</span> mid</span><br><span class="line"> }</span><br><span class="line"> l = mid + <span class="number">1</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> mid</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>这题使用二分法来找到结果。</p><h2 id="二进制问题"><a href="#二进制问题" class="headerlink" title="二进制问题"></a>二进制问题</h2><p>二进制问题,一般使用<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators" target="_blank" rel="noopener">按位运算符</a>和二进制转换 <code>Number.parseInt()</code> 和 <code>Number.prototype.toString()</code>来解决。</p><p><strong>将一个32位数字的二进制进行倒序</strong><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> reverseBits = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> t = n.toString(<span class="number">2</span>).split(<span class="string">""</span>);</span><br><span class="line"> <span class="keyword">while</span>(t.length < <span class="number">32</span>) t.unshift(<span class="string">"0"</span>); <span class="comment">// 插入足够的 0</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">parseInt</span>(t.reverse().join(<span class="string">""</span>), <span class="number">2</span>);</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><h1 id="常用算法"><a href="#常用算法" class="headerlink" title="常用算法"></a>常用算法</h1><p>讲了这么多,其实除了常用的排序、搜索,其他最常用的就是 DP、DFS、BFS 这三个算法了。可以这么说:掌握了排序和这三个算法,可以 AC 大多数的算法问题。这么牛逼的算法了解一下?</p><h2 id="简单说说几种排序和查找"><a href="#简单说说几种排序和查找" class="headerlink" title="简单说说几种排序和查找"></a>简单说说几种排序和查找</h2><ul><li><strong>冒泡排序</strong>:遍历数组,对比元素和后面相邻元素,如果当前元素大于后面元素,调换位置。这样从头遍历到尾,获取最后一位排序玩的元素。然后在 1 到 n - 1 中再次重复以上步骤。直到最后第一和第二个元素对比大小。是一种从后往前的排序。</li><li><p><strong>选择排序</strong>:遍历数组,找到最小的元素位置,与第一个元素调换位置,然后缩小范围从第二个元素开始遍历,如此重复到最后一个元素。可以从后往前也可以从前往后排序。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> len = arr.length</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < len; i++) {</span><br><span class="line"> <span class="keyword">let</span> min = i</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = i + <span class="number">1</span>; j < len; j++) {</span><br><span class="line"> <span class="keyword">if</span> (arr[j] < arr[min]) min = j</span><br><span class="line"> }</span><br><span class="line"> swap(arr, i, min)</span><br><span class="line"> <span class="built_in">console</span>.log(arr)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p><strong>插入排序</strong>:遍历数组,选中某一个元素,与前面相邻元素对比,如果当前元素小于之前元素,调换位置,继续对比直到当前元素前的元素小于当前元素(或者到最前面),如此对所有元素排序一遍。是一种从前往后的排序。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> len = arr.length</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i < len; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = i; j > <span class="number">0</span> && arr[j] < arr[j - <span class="number">1</span>]; j--) {</span><br><span class="line"> swap(arr, j, j - <span class="number">1</span>)</span><br><span class="line"> <span class="built_in">console</span>.log(arr)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p><strong>希尔排序</strong>:类似于插入排序,选中一个元素与元素前 n 个元素进行比大小和调换位置。之后再缩小 n 的值。这种方法可以减少插入排序中最小值在最后面,然后需要一个一个调换位置知道最前面这类问题。减少调换次数。是一种从前往后的排序。</p></li><li><p><strong>归并排序</strong>:在《算法》中提到了两种归并排序:一种是自上而下的归并排序。将数组不断二分到最小单位(1到2个元素)将他们进行排序,之后将前两个和后两个元素对比,如此往上最后完成整个数组的排序。还有一种自下而上的归并排序是直接将数组分割为若干个子数组进行排序然后合并。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> aux = <span class="keyword">new</span> <span class="built_in">Array</span>(arr.length)</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sort</span>(<span class="params">arr, lo, hi</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (hi <= lo) <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">let</span> mid = lo + (<span class="built_in">parseInt</span>((hi - lo) / <span class="number">2</span>))</span><br><span class="line"></span><br><span class="line"> sort(arr, lo, mid)</span><br><span class="line"> sort(arr, mid + <span class="number">1</span>, hi)</span><br><span class="line"> merge(arr, lo, mid, hi)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">merge</span>(<span class="params">arr, lo, mid, hi</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> i = lo, j = mid + <span class="number">1</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> k = lo; k <= hi; k++) {</span><br><span class="line"> aux[k] = arr[k]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> k = lo; k <= hi; k++) {</span><br><span class="line"> <span class="keyword">if</span> (i > mid) arr[k] = aux[j++]</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (j > hi) arr[k] = aux[i++]</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (aux[j] < aux[i]) arr[k] = aux[j++]</span><br><span class="line"> <span class="keyword">else</span> arr[k] = aux[i++]</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">console</span>.log(arr)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p><strong>快速排序</strong>:选定第一个值为中间值,然后将小于中间值的元素放在中间值的左侧而大于中间值的元素放在中间值右侧,然后对两侧的元素分别再次切割,直到最小单位。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sort</span>(<span class="params">arr, lo, hi</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (hi <= lo + <span class="number">1</span>) <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">let</span> mid = partition(arr, lo, hi) <span class="comment">// 切分方法</span></span><br><span class="line"> sort(arr, lo, mid)</span><br><span class="line"> sort(arr, mid + <span class="number">1</span>, hi)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">partition</span>(<span class="params">arr, lo, hi</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> i = lo, j = hi + <span class="number">1</span></span><br><span class="line"> <span class="keyword">let</span> v = arr[lo]</span><br><span class="line"> <span class="keyword">while</span>(<span class="literal">true</span>) {</span><br><span class="line"> <span class="keyword">while</span>(arr[++i] < v) <span class="keyword">if</span> (i == hi) <span class="keyword">break</span></span><br><span class="line"> <span class="keyword">while</span>(v < arr[--j]) <span class="keyword">if</span> (j == lo) <span class="keyword">break</span></span><br><span class="line"> <span class="keyword">if</span> ((i >= j)) <span class="keyword">break</span></span><br><span class="line"> swap(arr, i, j)</span><br><span class="line"> <span class="built_in">console</span>.log(arr)</span><br><span class="line"> }</span><br><span class="line"> swap(arr, lo, j)</span><br><span class="line"> <span class="built_in">console</span>.log(arr)</span><br><span class="line"> <span class="keyword">return</span> j</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p><strong>三向切分的快速排序</strong>:类似于快速排序,优化点在于如果某个元素等于切分元素,元素位置不变。最后小于切分元素的到左边,等于切分元素的根据数量放在中间,大于切分元素的放在右边。<strong>适用于有大量相同大小元素的数组。</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sort</span>(<span class="params">arr, lo, hi</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (hi <= lo) <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">let</span> lt = lo, i = lo + <span class="number">1</span>, gt = hi;</span><br><span class="line"> <span class="keyword">let</span> v = arr[lo]</span><br><span class="line"> <span class="keyword">while</span> (i <= gt) {</span><br><span class="line"> <span class="keyword">if</span> (arr[i] < v) swap(arr, lt++, i++)</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (arr[i] > v) swap(arr, i, gt--)</span><br><span class="line"> <span class="keyword">else</span> i++</span><br><span class="line"> <span class="built_in">console</span>.log(arr)</span><br><span class="line"> }</span><br><span class="line"> sort(arr, lo, lt - <span class="number">1</span>)</span><br><span class="line"> sort(arr, gt + <span class="number">1</span>, hi)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p><strong>堆排序</strong>:堆排序可以说是一种利用堆的概念来排序的选择排序。使用优先队列返回最大值的特性逐个返回当前堆的最大值。</p></li><li><strong>计数排序</strong>:就是将数组中所有元素的出现次数保存在一个数组中,然后按照从小到大返回排序后的数组。</li><li><strong>桶排序</strong>:其实就是字符串排序的 LSD 和 MSD 排序。LSD 使用索引计数法从字符串右边向左边移动,根据当前值进行排序。而 MSD 是从左到右使用索引计数法来排序,在字符串第一个字符后,将字符串数组分为若干个相同首字符串的数组各自进行第二、第三次的 MSD 排序。</li><li><strong>二分查找</strong>: 对有序数组去中间值与目标值相比对。如果目标值小于中间值,取前一半数组继续二分。如果目标值大于中间值,取后一半数组继续二分。如果目标值等于中间值,命中!</li></ul><h2 id="DP"><a href="#DP" class="headerlink" title="DP"></a>DP</h2><p>关于动态规划,可以看下<a href="http://www.cnblogs.com/little-YTMM/p/5372680.html" target="_blank" rel="noopener">详解动态规划——邹博讲动态规划</a>一文,其中讲了路径、硬币、最长子序列。都是 LeetCode 中有的题目。<br>我的理解:动态规划就是下一状态可以根据上一状态,或之前几个状态获取到的一种推理过程。</p><h2 id="DFS"><a href="#DFS" class="headerlink" title="DFS"></a>DFS</h2><p>深度优先搜索(DFS)就是选中某条从条件1到条件2的某条可能性进行搜索,之后返回搜索其他一条可能性,如此一条条升入。举个栗子,如果有5条路,那么 DFS 算法就是只排出一个斥候先走一条路走到底去侦察,如果走不通那么返回走下一条路径。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">DFS(顶点v)</span><br><span class="line">{</span><br><span class="line"> 标记v为已遍历;</span><br><span class="line"> <span class="keyword">for</span>(对于每一个邻接v且未标记遍历的点u)</span><br><span class="line"> DFS(u);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>DFS 使用的是递归的方式进行搜索的。</p><p><strong>示例:</strong>在二维字母矩阵中查找是否能够使用相邻字母组成目标单词。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> exist = <span class="function"><span class="keyword">function</span> (<span class="params">board, word</span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> y = <span class="number">0</span>; y < board.length; y++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> x = <span class="number">0</span>; x < board[<span class="number">0</span>].length; x++) {</span><br><span class="line"> <span class="keyword">if</span> (find(board, word, y, x, <span class="number">0</span>)) <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">find</span>(<span class="params">board, word, y, x, d</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (d == word.length) <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> <span class="keyword">if</span> (y < <span class="number">0</span> || x < <span class="number">0</span> || y == board.length || x == board[y].length) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">if</span> (board[y][x] != word[d]) <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"> <span class="keyword">let</span> tmp = board[y][x]</span><br><span class="line"> board[y][x] = <span class="string">"*"</span></span><br><span class="line"> <span class="keyword">let</span> exist = find(board, word, y, x + <span class="number">1</span>, d + <span class="number">1</span>)</span><br><span class="line"> || find(board, word, y, x - <span class="number">1</span>, d + <span class="number">1</span>)</span><br><span class="line"> || find(board, word, y + <span class="number">1</span>, x, d + <span class="number">1</span>)</span><br><span class="line"> || find(board, word, y - <span class="number">1</span>, x, d + <span class="number">1</span>)</span><br><span class="line"> board[y][x] = tmp</span><br><span class="line"> <span class="keyword">return</span> exist</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="BFS"><a href="#BFS" class="headerlink" title="BFS"></a>BFS</h2><p>广度优先搜索(BFS)就是将从条件1到条件2的所有可能性都列出来同步搜索的过程。适用于查找最短路径。举个栗子,如果有5条路,那么 BFS 算法就是分别向5条路排出斥候去侦察。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">BFS()</span><br><span class="line">{</span><br><span class="line"> 输入起始点;</span><br><span class="line"> 初始化所有顶点标记为未遍历;</span><br><span class="line"> 初始化一个队列queue并将起始点放入队列;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span>(queue不为空)</span><br><span class="line"> {</span><br><span class="line"></span><br><span class="line"> 从队列中删除一个顶点s并标记为已遍历; </span><br><span class="line"> 将s邻接的所有还没遍历的点加入队列;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>BFS是使用数组存储下一顶点的方式。</p><p><strong>示例:</strong>每次改变一次字母,通过给定数组中的单词,从单词 A 变为单词 B。(<a href="https://leetcode.com/problems/word-ladder/description/" target="_blank" rel="noopener">127题</a>)<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @param {string} beginWord</span></span><br><span class="line"><span class="comment"> * @param {string} endWord</span></span><br><span class="line"><span class="comment"> * @param {string[]} wordList</span></span><br><span class="line"><span class="comment"> * @return {number}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> ladderLength = <span class="function"><span class="keyword">function</span> (<span class="params">beginWord, endWord, wordList</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!wordList.includes(endWord)) <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"> <span class="keyword">let</span> set = <span class="keyword">new</span> <span class="built_in">Set</span>(),</span><br><span class="line"> visited = <span class="keyword">new</span> <span class="built_in">Set</span>(),</span><br><span class="line"> len = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> set.add(beginWord)</span><br><span class="line"> visited.add(beginWord)</span><br><span class="line"> <span class="keyword">while</span> (set.size != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">let</span> tmp = <span class="keyword">new</span> <span class="built_in">Set</span>([...set])</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> w <span class="keyword">of</span> tmp) {</span><br><span class="line"> visited.add(w)</span><br><span class="line"> set.delete(w)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (changeOneChar(w, endWord))</span><br><span class="line"> <span class="keyword">return</span> len + <span class="number">1</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> word <span class="keyword">of</span> wordList){</span><br><span class="line"> <span class="keyword">if</span> (changeOneChar(w, word) && !visited.has(word)){</span><br><span class="line"> set.add(word)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> len++</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">changeOneChar</span>(<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> count = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < a.length; i++)</span><br><span class="line"> <span class="keyword">if</span> (a[i] != b[i])</span><br><span class="line"> count++</span><br><span class="line"> <span class="keyword">return</span> count == <span class="number">1</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>写下 AC 一遍题目之后的收获。</p><ul><li>知道了方法论,做起题来轻松了不少。</li><li>遇到问题多找轮子,一定有某种方法论可以用。</li><li>不要耍小聪明用一些奇巧淫技,思路不对再怎么绕都是浪费时间。</li><li>不要想着自己造轮子(特别是算法方面),绝大多数问题前辈一定有更好更完善的方案在。自己造轮子费时费事又没太大意义。</li><li>看答案和自己做是两回事,自己动手实现了才能算是会了。</li><li>算法之所以存在,就是用来适应某些场景、解决某类问题的。在对的场景选择对的算法才能体现算法的价值,不要滥用算法。</li><li>没必要把所有算法都精通,但起码在遇到问题时可以找到最优算法解决问题。即知道算法的存在及其用途,按需深入学习。</li></ul><p>其实刷算法题还是很有趣的事情,之后计划把 <a href="https://leetcode.com/problemset/all/" target="_blank" rel="noopener">LeetCode 题库</a>中的所有问题都刷一遍~</p><p><strong>PS:本文以及相关项目中有任何错误或者可以改进的地方,还请提出。共同进步~</strong></p>]]></content>
<summary type="html">
<blockquote>
<p>花了十几天,把《算法》看了一遍然后重新 AC 了一遍 LeetCode 的题,收获颇丰。这次好好记录下心得。<br>我把所有做题的代码都放在 github 上以供参考。<br>项目地址:<a href="https://github.com/vio
</summary>
</entry>
<entry>
<title>element 源码学习六 —— Carousel 走马灯学习</title>
<link href="https://violetjack.github.io/2018/04/13/element%20%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E5%85%AD%20%E2%80%94%E2%80%94%20Carousel%20%E8%B5%B0%E9%A9%AC%E7%81%AF%E5%AD%A6%E4%B9%A0/"/>
<id>https://violetjack.github.io/2018/04/13/element 源码学习六 —— Carousel 走马灯学习/</id>
<published>2018-04-12T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.255Z</updated>
<content type="html"><![CDATA[<h1 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h1><p>走马灯功能在展示图片时经常用到,而 element 中提供了 Carousel 组件。出于好奇学习一下它的实现原理。<br>具体的功能详情请查阅<a href="http://element-cn.eleme.io/#/zh-CN/component/carousel" target="_blank" rel="noopener">官方文档</a>。<br>关于组件属性,该组件提供了组件高度、索引、指示器、切换时间等一众配置,这个只要动手试一遍都能理解。<br>关于事件,提供了一个 <code>change</code> 事件。可以通过 <code>v-on:change="changeFun"</code> 事件绑定来监听。该事件传递了两个参数:当前页索引和前一页索引。参考源码中的 <code>$emit</code> 方法:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">watch: {</span><br><span class="line"> activeIndex(val, oldVal) {</span><br><span class="line"> <span class="keyword">this</span>.resetItemPosition(oldVal);</span><br><span class="line"> <span class="keyword">this</span>.$emit(<span class="string">'change'</span>, val, oldVal);</span><br><span class="line"> },</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>关于方法,组件提供了三个方法用于操作组件页面的切换。使用方法是通过 $ref <a href="https://cn.vuejs.org/v2/guide/components.html#子组件引用" target="_blank" rel="noopener">子组件引用</a>来访问子组件,执行其方法。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 假设为 el-carousel 设置了 ref="car"</span></span><br><span class="line"><span class="comment">// setActiveItem手动切换幻灯片需要切换的幻灯片的索引,从 0 开始;或相应 el-carousel-item 的 name 属性值</span></span><br><span class="line"><span class="comment">// prev切换至上一张幻灯片</span></span><br><span class="line"><span class="comment">// next 切换至下一张幻灯片</span></span><br><span class="line">pre() {</span><br><span class="line"> <span class="keyword">this</span>.$refs.car.prev()</span><br><span class="line">},</span><br><span class="line">next() {</span><br><span class="line"> <span class="keyword">this</span>.$refs.car.next()</span><br><span class="line">},</span><br><span class="line">first() {</span><br><span class="line"> <span class="keyword">this</span>.$refs.car.setActiveItem(<span class="number">0</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h1 id="源码位置"><a href="#源码位置" class="headerlink" title="源码位置"></a>源码位置</h1><p>Carousel 的源码位于 <code>package/carousel/</code> 目录下。源码目录如下:<br><img src="https://upload-images.jianshu.io/upload_images/1987062-08d5f9051a1f9174.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="源码目录"></p><p>简单说下目录内容:</p><ul><li><strong>item.vue</strong> carousel-item 组件代码</li><li><strong>main.vue</strong> carousel 组件代码</li><li><strong>_index.js</strong> 导出 carousel-item 和 carousel </li><li><strong>cooking.conf.js</strong> cooking 配置</li><li><strong>index.js</strong> 导出 carousel 组件</li><li><strong>package.json</strong> 组件信息</li><li><strong>index.js</strong> 导出 carousel-item 组件</li></ul><p>其实主要就是两个 <code>.vue</code> 文件,其他都是些配置、导出的功能文件,这里可以忽略不看~</p><h1 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h1><p>老规矩,我们通过几个问题切入去看源代码。</p><h2 id="1-基本原理:页面切换-指示器-切换按钮的实现"><a href="#1-基本原理:页面切换-指示器-切换按钮的实现" class="headerlink" title="1. 基本原理:页面切换 + 指示器 + 切换按钮的实现"></a>1. 基本原理:页面切换 + 指示器 + 切换按钮的实现</h2><h3 id="页面切换"><a href="#页面切换" class="headerlink" title="页面切换"></a>页面切换</h3><p>看了一下 DOM 的 Elements 排列,<strong>发现页面切换使用的是 transform 2D 转换和 transition 过渡。</strong><br>一般页面切换其实是几个页面使用 translateX 进行位移切换。<br><img src="https://upload-images.jianshu.io/upload_images/1987062-eb167cd5660779e4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="一般页面结构"></p><p>而卡片变化切换同时使用了 translateX 位移和 scale 缩放实现中间一张卡最大,左右卡片小的效果的。<br><img src="https://upload-images.jianshu.io/upload_images/1987062-974136def5c81522.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="卡片化"></p><p>而卡片的层叠使用 z-index 值得大小来实现。<br><strong>所以,逻辑计算只需要根据当前显示页面计算下每个页面的位移值。</strong>而 carousel-item 中也的确有计算的逻辑:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// item.vue</span></span><br><span class="line"><span class="comment">// index 当前 item 索引</span></span><br><span class="line"><span class="comment">// activeIndex 激活的 item 索引</span></span><br><span class="line"><span class="comment">// oldIndex 之前 item 索引</span></span><br><span class="line">translateItem(index, activeIndex, oldIndex) {</span><br><span class="line"> <span class="comment">// 获取父元素宽度 https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth</span></span><br><span class="line"> <span class="keyword">const</span> parentWidth = <span class="keyword">this</span>.$parent.$el.offsetWidth;</span><br><span class="line"> <span class="comment">// 获取 item 页面数量</span></span><br><span class="line"> <span class="keyword">const</span> length = <span class="keyword">this</span>.$parent.items.length;</span><br><span class="line"> <span class="comment">// 判断是否需要过渡动画 class</span></span><br><span class="line"> <span class="comment">// .el-carousel__item.is-animating {</span></span><br><span class="line"> <span class="comment">// transition: transform 0.4s ease-in-out;</span></span><br><span class="line"> <span class="comment">// }</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.$parent.type !== <span class="string">"card"</span> && oldIndex !== <span class="literal">undefined</span>) {</span><br><span class="line"> <span class="keyword">this</span>.animating = index === activeIndex || index === oldIndex;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 处理 index</span></span><br><span class="line"> <span class="keyword">if</span> (index !== activeIndex && length > <span class="number">2</span>) {</span><br><span class="line"> index = <span class="keyword">this</span>.processIndex(index, activeIndex, length);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.$parent.type === <span class="string">"card"</span>) {</span><br><span class="line"> <span class="comment">// 卡片化</span></span><br><span class="line"> <span class="keyword">this</span>.inStage = <span class="built_in">Math</span>.round(<span class="built_in">Math</span>.abs(index - activeIndex)) <= <span class="number">1</span>; <span class="comment">// 激活组件及其前后组件定义 cursor: pointer; z-index: 1;</span></span><br><span class="line"> <span class="keyword">this</span>.active = index === activeIndex; <span class="comment">// 激活 class</span></span><br><span class="line"> <span class="comment">// 计算卡片化偏移量</span></span><br><span class="line"> <span class="keyword">this</span>.translate = <span class="keyword">this</span>.calculateTranslate(</span><br><span class="line"> index,</span><br><span class="line"> activeIndex,</span><br><span class="line"> parentWidth</span><br><span class="line"> );</span><br><span class="line"> <span class="comment">// 激活卡片不缩放,其他卡片缩放为 0.83</span></span><br><span class="line"> <span class="keyword">this</span>.scale = <span class="keyword">this</span>.active ? <span class="number">1</span> : CARD_SCALE;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 非卡片化</span></span><br><span class="line"> <span class="keyword">this</span>.active = index === activeIndex; <span class="comment">// 激活 class</span></span><br><span class="line"> <span class="keyword">this</span>.translate = parentWidth * (index - activeIndex); <span class="comment">// 计算位移 根据父组件宽度计算</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 计算完后显示</span></span><br><span class="line"> <span class="keyword">this</span>.ready = <span class="literal">true</span>;</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><h3 id="指示器"><a href="#指示器" class="headerlink" title="指示器"></a>指示器</h3><p>指示器就是多个 button 组成的横向列表,根据当前显示页面修改某个指示器背景颜色。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ul</span></span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"el-carousel__indicators"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-if</span>=<span class="string">"indicatorPosition !== 'none'"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:class</span>=<span class="string">"{ 'el-carousel__indicators--labels': hasLabel, 'el-carousel__indicators--outside': indicatorPosition === 'outside' || type === 'card' }"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-for</span>=<span class="string">"(item, index) in items"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"el-carousel__indicator"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:class</span>=<span class="string">"{ 'is-active': index === activeIndex }"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">mouseenter</span>=<span class="string">"throttledIndicatorHover(index)"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">click.stop</span>=<span class="string">"handleIndicatorClick(index)"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">class</span>=<span class="string">"el-carousel__button"</span>></span><span class="tag"><<span class="name">span</span> <span class="attr">v-if</span>=<span class="string">"hasLabel"</span>></span>{{ item.label }}<span class="tag"></<span class="name">span</span>></span><span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">li</span>></span></span><br></pre></td></tr></table></figure></p><h3 id="切换按钮"><a href="#切换按钮" class="headerlink" title="切换按钮"></a>切换按钮</h3><p>切换按钮其实就是两个 absolute 的按钮,点击实现页面切换即可。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">transition</span> <span class="attr">name</span>=<span class="string">"carousel-arrow-left"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span></span></span><br><span class="line"><span class="tag"> <span class="attr">type</span>=<span class="string">"button"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-if</span>=<span class="string">"arrow !== 'never'"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-show</span>=<span class="string">"arrow === 'always' || hover"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">mouseenter</span>=<span class="string">"handleButtonEnter('left')"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">mouseleave</span>=<span class="string">"handleButtonLeave"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">click.stop</span>=<span class="string">"throttledArrowClick(activeIndex - 1)"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"el-carousel__arrow el-carousel__arrow--left"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"el-icon-arrow-left"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">button</span>></span></span><br><span class="line"><span class="tag"></<span class="name">transition</span>></span></span><br><span class="line"><span class="tag"><<span class="name">transition</span> <span class="attr">name</span>=<span class="string">"carousel-arrow-right"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span></span></span><br><span class="line"><span class="tag"> <span class="attr">type</span>=<span class="string">"button"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-if</span>=<span class="string">"arrow !== 'never'"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-show</span>=<span class="string">"arrow === 'always' || hover"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">mouseenter</span>=<span class="string">"handleButtonEnter('right')"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">mouseleave</span>=<span class="string">"handleButtonLeave"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">click.stop</span>=<span class="string">"throttledArrowClick(activeIndex + 1)"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"el-carousel__arrow el-carousel__arrow--right"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"el-icon-arrow-right"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">button</span>></span></span><br><span class="line"><span class="tag"></<span class="name">transition</span>></span></span><br></pre></td></tr></table></figure></p><p>这里要注意的就是按钮外包裹了 <transition> 标签来实现按钮进入和离开的过渡效果。</transition></p><h2 id="2-页面切换动画如何实现?"><a href="#2-页面切换动画如何实现?" class="headerlink" title="2. 页面切换动画如何实现?"></a>2. 页面切换动画如何实现?</h2><p>其实在基本原理里面都提到了,可以从源码中找到两个样式:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"># 内联样式</span><br><span class="line"><span class="selector-tag">element</span><span class="selector-class">.style</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translateX</span>(888.56px) <span class="built_in">scale</span>(0.83);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"># 过渡样式</span><br><span class="line"><span class="selector-class">.is-animating</span> {</span><br><span class="line"> <span class="attribute">transition</span>: transform <span class="number">0.4s</span> ease-in-out;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>静态情况下,使用 transitionX 维持页面位置,显示当前页面。过渡行为时,加入 0.4 秒的 transition 过渡效果。</p><h2 id="3-卡片化如何实现效果?"><a href="#3-卡片化如何实现效果?" class="headerlink" title="3. 卡片化如何实现效果?"></a>3. 卡片化如何实现效果?</h2><p>卡片化切换其实和一般的切换差不多,只是显示卡片从一张变为三张。中间的卡片 z-index 为 2,而左右的卡片 z-index 为 1,从而实现中间卡片在前面的样式。而计算三张卡片位置的方法如下:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">calculateTranslate(index, activeIndex, parentWidth) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.inStage) {</span><br><span class="line"> <span class="keyword">return</span> parentWidth * ((<span class="number">2</span> - CARD_SCALE) * (index - activeIndex) + <span class="number">1</span>) / <span class="number">4</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (index < activeIndex) {</span><br><span class="line"> <span class="keyword">return</span> -(<span class="number">1</span> + CARD_SCALE) * parentWidth / <span class="number">4</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> (<span class="number">3</span> + CARD_SCALE) * parentWidth / <span class="number">4</span>;</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>而卡片的宽度为容器宽度的一半~左右两边的卡片缩放了 83% 。<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.el-carousel__item--card</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">transition</span>: transform <span class="number">0.4s</span> ease-in-out;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">element</span><span class="selector-class">.style</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translateX</span>(-39.44px) <span class="built_in">scale</span>(0.83)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>总结下:</p><ul><li>显示三张卡片。</li><li>三张卡片高度跟随容器,宽度为容器的 50%。</li><li>左右两张卡片缩放了 83% 大小。</li><li>左右两张卡片 z-index 为 1;中间卡片 z-index 为 2。</li></ul><h2 id="4-按钮出现和消失的效果如何实现?"><a href="#4-按钮出现和消失的效果如何实现?" class="headerlink" title="4. 按钮出现和消失的效果如何实现?"></a>4. 按钮出现和消失的效果如何实现?</h2><p>使用了 vue 的 transition 标签来实现。具体效果为:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.el-carousel__arrow</span> {</span><br><span class="line"> <span class="attribute">border</span>: none;</span><br><span class="line"> <span class="attribute">outline</span>: none;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">36px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">36px</span>;</span><br><span class="line"> <span class="attribute">cursor</span>: pointer;</span><br><span class="line"> <span class="attribute">transition</span>: <span class="number">0.3s</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="built_in">rgba</span>(31, 45, 61, 0.11);</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">z-index</span>: <span class="number">10</span>;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-50%);</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">12px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.carousel-arrow-left-enter</span>,</span><br><span class="line"><span class="selector-class">.carousel-arrow-left-leave-active</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-50%) <span class="built_in">translateX</span>(-10px);</span><br><span class="line"> <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.carousel-arrow-right-enter</span>,</span><br><span class="line"><span class="selector-class">.carousel-arrow-right-leave-active</span> {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translateY</span>(-50%) <span class="built_in">translateX</span>(10px);</span><br><span class="line"> <span class="attribute">opacity</span>: <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>实现的效果为:左边箭头向从左边 10px 位置进入和离开,而右边箭头从右边 10px 位置进入和离开。而 <code>translateY</code> 是为了垂直居中。</p><h2 id="5-如何实现自动切换功能?"><a href="#5-如何实现自动切换功能?" class="headerlink" title="5. 如何实现自动切换功能?"></a>5. 如何实现自动切换功能?</h2><p>使用 setInterval 方法来实现定时向后切换页面。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">playSlides() {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.activeIndex < <span class="keyword">this</span>.items.length - <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">this</span>.activeIndex++;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 如果是最后一页则跳转到第一页</span></span><br><span class="line"> <span class="keyword">this</span>.activeIndex = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line">},</span><br><span class="line"><span class="comment">// 取消 timer</span></span><br><span class="line">pauseTimer() {</span><br><span class="line"> clearInterval(<span class="keyword">this</span>.timer);</span><br><span class="line">},</span><br><span class="line"><span class="comment">// 开始 timer</span></span><br><span class="line">startTimer() {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.interval <= <span class="number">0</span> || !<span class="keyword">this</span>.autoplay) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">this</span>.timer = setInterval(<span class="keyword">this</span>.playSlides, <span class="keyword">this</span>.interval);</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><h1 id="自己实现个走马灯玩玩"><a href="#自己实现个走马灯玩玩" class="headerlink" title="自己实现个走马灯玩玩~"></a>自己实现个走马灯玩玩~</h1><p>学以致用,这里写个简单的 demo,实现下走马灯功能。<br>例一:<strong><a href="https://jsfiddle.net/VioletJack/zkjtchL2/6/" target="_blank" rel="noopener">走马灯效果</a></strong><br>例二:<strong><a href="https://jsfiddle.net/VioletJack/jfvf6xmq/3/" target="_blank" rel="noopener">卡片化走马灯效果</a></strong><br>其中,例一和例二实现了走马灯和卡片化的自动切换、手动切换、切换效果。<br>这里留了一个问题待解决:</p><ul><li>走马灯中第一页和最后一页的切换如何做到更好?</li></ul><p>这些问题将会尽快解决!</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>至此,我们了解了走马灯的实现原理,以及一些小功能的实现。最后也用两个例子证明了我们的分析。</p><h1 id="Vue-实验室"><a href="#Vue-实验室" class="headerlink" title="Vue 实验室"></a>Vue 实验室</h1><p>《Vue 实验室》 至今也写了不少了,作者将在把最后几篇计划中的博客写完后,以当前的知识认知水平重新改进《Vue 实验室》往期的文章,并整理到 gitbook 中便于读者阅读,敬请期待。</p>]]></content>
<summary type="html">
<h1 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h1><p>走马灯功能在展示图片时经常用到,而 element 中提供了 Carousel 组件。出于好奇学习一下它的实现原理。<br>
</summary>
</entry>
<entry>
<title>Vue.js 源码学习九 —— 过渡效果 transition 学习</title>
<link href="https://violetjack.github.io/2018/04/12/Vue.js%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%B9%9D%20%E2%80%94%E2%80%94%20%E8%BF%87%E6%B8%A1%E6%95%88%E6%9E%9C%20transition%20%E5%AD%A6%E4%B9%A0/"/>
<id>https://violetjack.github.io/2018/04/12/Vue.js源码学习九 —— 过渡效果 transition 学习/</id>
<published>2018-04-11T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.253Z</updated>
<content type="html"><![CDATA[<blockquote><p>在学习 element ui 时,发现组件的过渡用的是 Vue.js 提供的 <transition> 标签。这里来好好认识下 vue 的过渡到底是如何工作的。</transition></p></blockquote><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>废话不多说,详细的内容请看<a href="https://cn.vuejs.org/v2/guide/transitions.html" target="_blank" rel="noopener">官方文档</a>,里面有详细的分析和例子够你看懂了(就是费时间~)。简单说说我对 vue 过渡的理解。经过一下午的折腾,总结出以下几点:</p><ul><li><strong>有四种情况会触发过渡效果:</strong><br>1 v-if<br>2 v-show<br>3 动态组件(如 component 的 is 属性)<br>4 组件根节点发生变化(如 v-if v-else 切换根节点)</li><li><strong>过渡效果 CSS 命名规律:</strong>(name 属性,默认为 v)-(行为:enter、leave、appear、move)-(阶段:无、active、to)</li><li><strong>有三种方式来设置过渡样式:</strong><br>1 为 <transition> 标签设定 name 属性。<br>2 在 <transition> 标签中插入 <code>enter-active-class</code> 等设置自定义过渡类名。<br>3 使用 JavaScript 在过渡的<a href="https://cn.vuejs.org/v2/guide/transitions.html#JavaScript-%E9%92%A9%E5%AD%90" target="_blank" rel="noopener">钩子</a>处修改过渡样式。</transition></transition></li><li><strong>个人理解:<code><transition></code> 标签用于单个元素的进入和离开效果。<code><transition-group></code> 标签用于处理如 <code>v-for</code> 遍历这样多个元素的过渡动画。</strong></li></ul><h1 id="自己实现个过渡方法"><a href="#自己实现个过渡方法" class="headerlink" title="自己实现个过渡方法"></a>自己实现个过渡方法</h1><p>先来两个简单例子理解下 transition(为了节省篇幅和便于查看写在 JSFiddle 中)有兴趣的朋友可以看下~<br><strong>例1:<a href="https://jsfiddle.net/VioletJack/0pnqc99z/1/" target="_blank" rel="noopener">v-enter 和 v-leave 简单实现</a></strong><br><strong>例2:<a href="https://jsfiddle.net/VioletJack/3takx674/" target="_blank" rel="noopener">v-move 简单实现</a></strong></p><h1 id="transition-学习"><a href="#transition-学习" class="headerlink" title="transition 学习"></a>transition 学习</h1><h2 id="1-基本原理是什么?"><a href="#1-基本原理是什么?" class="headerlink" title="1. 基本原理是什么?"></a>1. 基本原理是什么?</h2><p>基本原理还是 CSS3 的 <code>transition</code>、<code>transform</code>、<code>animation</code> 这几个属性。用户定义过渡效果,Vue.js 进行处理。下面我们通过 <transition> 过渡的进入的过程看一下:</transition></p><ul><li>插入元素</li><li>解析 <transition> 标签,获取对应的过渡类名。这里默认就 <code>v-</code> 开头了。</transition></li><li>为元素定义 v-enter 和 v-enter-active 两个类。<code>class="v-enter v-enter-active"</code>。</li><li>下一帧移除 v-enter,添加 v-enter-to。<code>class="v-enter-active v-enter-to"</code>。</li><li>获取过渡时间,延时执行回调函数。</li><li>回调函数中移除 v-enter、v-enter-active 和 v-enter-to 的这些过渡类名,完成过渡。</li><li>在整个过程中调用了 <code>beforeEnterHook</code> 、<code>enterHook</code>、<code>afterEnterHook</code>、<code>enterCancelledHook</code> 四个函数,执行相应的<a href="https://cn.vuejs.org/v2/guide/transitions.html#JavaScript-钩子" target="_blank" rel="noopener"> JavaScript 钩子</a>。</li></ul><p>下面是 <code>enter</code> 函数的代码及注释:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 进入过渡效果</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">enter</span> (<span class="params">vnode: VNodeWithData, toggleDisplay: ?(</span>) => <span class="title">void</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> el: any = vnode.elm</span><br><span class="line"></span><br><span class="line"> <span class="comment">// call leave callback now 执行 leave 回调函数</span></span><br><span class="line"> <span class="keyword">if</span> (isDef(el._leaveCb)) {</span><br><span class="line"> el._leaveCb.cancelled = <span class="literal">true</span></span><br><span class="line"> el._leaveCb()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 解析 transition 的数据(class、tag、name等)</span></span><br><span class="line"> <span class="keyword">const</span> data = resolveTransition(vnode.data.transition)</span><br><span class="line"> <span class="keyword">if</span> (isUndef(data)) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* istanbul ignore if */</span></span><br><span class="line"> <span class="keyword">if</span> (isDef(el._enterCb) || el.nodeType !== <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> {</span><br><span class="line"> css,</span><br><span class="line"> type,</span><br><span class="line"> enterClass,</span><br><span class="line"> enterToClass,</span><br><span class="line"> enterActiveClass,</span><br><span class="line"> appearClass,</span><br><span class="line"> appearToClass,</span><br><span class="line"> appearActiveClass,</span><br><span class="line"> beforeEnter,</span><br><span class="line"> enter,</span><br><span class="line"> afterEnter,</span><br><span class="line"> enterCancelled,</span><br><span class="line"> beforeAppear,</span><br><span class="line"> appear,</span><br><span class="line"> afterAppear,</span><br><span class="line"> appearCancelled,</span><br><span class="line"> duration</span><br><span class="line"> } = data</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将作为子组件的根节点放置时,我们需要检查 <transition> 的父元素是否出现检查。</span></span><br><span class="line"> <span class="keyword">let</span> context = activeInstance</span><br><span class="line"> <span class="keyword">let</span> transitionNode = activeInstance.$vnode</span><br><span class="line"> <span class="keyword">while</span> (transitionNode && transitionNode.parent) {</span><br><span class="line"> transitionNode = transitionNode.parent</span><br><span class="line"> context = transitionNode.context</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> isAppear = !context._isMounted || !vnode.isRootInsert</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (isAppear && !appear && appear !== <span class="string">''</span>) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取进入的 class</span></span><br><span class="line"> <span class="comment">// v-enter</span></span><br><span class="line"> <span class="keyword">const</span> startClass = isAppear && appearClass</span><br><span class="line"> ? appearClass</span><br><span class="line"> : enterClass</span><br><span class="line"> <span class="comment">// v-enter-active</span></span><br><span class="line"> <span class="keyword">const</span> activeClass = isAppear && appearActiveClass</span><br><span class="line"> ? appearActiveClass</span><br><span class="line"> : enterActiveClass</span><br><span class="line"> <span class="comment">// v-enter-to</span></span><br><span class="line"> <span class="keyword">const</span> toClass = isAppear && appearToClass</span><br><span class="line"> ? appearToClass</span><br><span class="line"> : enterToClass</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 4个生命周期钩子函数</span></span><br><span class="line"> <span class="keyword">const</span> beforeEnterHook = isAppear</span><br><span class="line"> ? (beforeAppear || beforeEnter)</span><br><span class="line"> : beforeEnter</span><br><span class="line"> <span class="keyword">const</span> enterHook = isAppear</span><br><span class="line"> ? (<span class="keyword">typeof</span> appear === <span class="string">'function'</span> ? appear : enter)</span><br><span class="line"> : enter</span><br><span class="line"> <span class="keyword">const</span> afterEnterHook = isAppear</span><br><span class="line"> ? (afterAppear || afterEnter)</span><br><span class="line"> : afterEnter</span><br><span class="line"> <span class="keyword">const</span> enterCancelledHook = isAppear</span><br><span class="line"> ? (appearCancelled || enterCancelled)</span><br><span class="line"> : enterCancelled</span><br><span class="line"></span><br><span class="line"> <span class="comment">// https://cn.vuejs.org/v2/guide/transitions.html#显性的过渡持续时间</span></span><br><span class="line"> <span class="keyword">const</span> explicitEnterDuration: any = toNumber(</span><br><span class="line"> isObject(duration)</span><br><span class="line"> ? duration.enter</span><br><span class="line"> : duration</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span> && explicitEnterDuration != <span class="literal">null</span>) {</span><br><span class="line"> checkDuration(explicitEnterDuration, <span class="string">'enter'</span>, vnode)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> expectsCSS = css !== <span class="literal">false</span> && !isIE9</span><br><span class="line"> <span class="keyword">const</span> userWantsControl = getHookArgumentsLength(enterHook)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 完成进入过渡后的回调函数</span></span><br><span class="line"> <span class="keyword">const</span> cb = el._enterCb = once(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (expectsCSS) {</span><br><span class="line"> <span class="comment">// 移除 v-enter-to 和 v-enter-active</span></span><br><span class="line"> removeTransitionClass(el, toClass)</span><br><span class="line"> removeTransitionClass(el, activeClass)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (cb.cancelled) {</span><br><span class="line"> <span class="keyword">if</span> (expectsCSS) {</span><br><span class="line"> <span class="comment">// 移除 v-enter</span></span><br><span class="line"> removeTransitionClass(el, startClass)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 调用 enter-cancelled</span></span><br><span class="line"> enterCancelledHook && enterCancelledHook(el)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> afterEnterHook && afterEnterHook(el)</span><br><span class="line"> }</span><br><span class="line"> el._enterCb = <span class="literal">null</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!vnode.data.show) {</span><br><span class="line"> <span class="comment">// 通过注入一个 insert 钩子,将待处理的 leave 元素移除。</span></span><br><span class="line"> mergeVNodeHook(vnode, <span class="string">'insert'</span>, () => {</span><br><span class="line"> <span class="keyword">const</span> parent = el.parentNode</span><br><span class="line"> <span class="keyword">const</span> pendingNode = parent && parent._pending && parent._pending[vnode.key]</span><br><span class="line"> <span class="keyword">if</span> (pendingNode &&</span><br><span class="line"> pendingNode.tag === vnode.tag &&</span><br><span class="line"> pendingNode.elm._leaveCb</span><br><span class="line"> ) {</span><br><span class="line"> pendingNode.elm._leaveCb()</span><br><span class="line"> }</span><br><span class="line"> enterHook && enterHook(el, cb)</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// start enter transition</span></span><br><span class="line"> beforeEnterHook && beforeEnterHook(el)</span><br><span class="line"> <span class="comment">// 预期 CSS</span></span><br><span class="line"> <span class="keyword">if</span> (expectsCSS) {</span><br><span class="line"> <span class="comment">// 添加 v-enter v-enter-active</span></span><br><span class="line"> addTransitionClass(el, startClass)</span><br><span class="line"> addTransitionClass(el, activeClass)</span><br><span class="line"> <span class="comment">// 下一帧</span></span><br><span class="line"> nextFrame(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="comment">// 移除 v-enter</span></span><br><span class="line"> removeTransitionClass(el, startClass)</span><br><span class="line"> <span class="keyword">if</span> (!cb.cancelled) {</span><br><span class="line"> <span class="comment">// 添加 v-enter-to</span></span><br><span class="line"> addTransitionClass(el, toClass)</span><br><span class="line"> <span class="keyword">if</span> (!userWantsControl) {</span><br><span class="line"> <span class="comment">// 预期进入时间</span></span><br><span class="line"> <span class="keyword">if</span> (isValidDuration(explicitEnterDuration)) {</span><br><span class="line"> setTimeout(cb, explicitEnterDuration)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 当 transition 结束</span></span><br><span class="line"> whenTransitionEnds(el, type, cb)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (vnode.data.show) {</span><br><span class="line"> toggleDisplay && toggleDisplay()</span><br><span class="line"> enterHook && enterHook(el, cb)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!expectsCSS && !userWantsControl) {</span><br><span class="line"> cb()</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="2-过渡的类名和自定义过渡的类名如何用于-中?"><a href="#2-过渡的类名和自定义过渡的类名如何用于-中?" class="headerlink" title="2. 过渡的类名和自定义过渡的类名如何用于 中?"></a>2. <a href="https://cn.vuejs.org/v2/guide/transitions.html#过渡的类名" target="_blank" rel="noopener">过渡的类名</a>和<a href="https://cn.vuejs.org/v2/guide/transitions.html#自定义过渡的类名" target="_blank" rel="noopener">自定义过渡的类名</a>如何用于 <transition> 中?</transition></h2><p>在 <a href="https://cn.vuejs.org/v2/api/#transition" target="_blank" rel="noopener"><transition></transition></a> 中一共有如下属性(props):<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> transitionProps = {</span><br><span class="line"> name: <span class="built_in">String</span>,</span><br><span class="line"> appear: <span class="built_in">Boolean</span>,</span><br><span class="line"> css: <span class="built_in">Boolean</span>,</span><br><span class="line"> mode: <span class="built_in">String</span>,</span><br><span class="line"> type: <span class="built_in">String</span>,</span><br><span class="line"> enterClass: <span class="built_in">String</span>,</span><br><span class="line"> leaveClass: <span class="built_in">String</span>,</span><br><span class="line"> enterToClass: <span class="built_in">String</span>,</span><br><span class="line"> leaveToClass: <span class="built_in">String</span>,</span><br><span class="line"> enterActiveClass: <span class="built_in">String</span>,</span><br><span class="line"> leaveActiveClass: <span class="built_in">String</span>,</span><br><span class="line"> appearClass: <span class="built_in">String</span>,</span><br><span class="line"> appearActiveClass: <span class="built_in">String</span>,</span><br><span class="line"> appearToClass: <span class="built_in">String</span>,</span><br><span class="line"> duration: [<span class="built_in">Number</span>, <span class="built_in">String</span>, <span class="built_in">Object</span>]</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>可以看到其中就有这些自定义过渡类名,如 enterClass。这些属性如被传入到 <transition> 子组件的 data.transition 对象中。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// extractTransitionData 函数返回组件的所有 propsData 和 listener</span></span><br><span class="line"><span class="keyword">const</span> data: <span class="built_in">Object</span> = (child.data || (child.data = {})).transition = extractTransitionData(<span class="keyword">this</span>)</span><br></pre></td></tr></table></figure></transition></p><p>而这个 data.transition 对象在 enter 函数中用到:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> data = resolveTransition(vnode.data.transition)</span><br></pre></td></tr></table></figure></p><p><code>resolveTransition</code> 函数:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">// 解析 transition 过渡 CSS</span><br><span class="line">export function resolveTransition (def?: string | Object): ?Object {</span><br><span class="line"> if (!def) {</span><br><span class="line"> return</span><br><span class="line"> }</span><br><span class="line"> // 合并过渡类名和自定义过渡类名</span><br><span class="line"> if (typeof def === 'object') {</span><br><span class="line"> const res = {}</span><br><span class="line"> if (def.css !== false) {</span><br><span class="line"> // 使用 name,默认为 v</span><br><span class="line"> extend(res, autoCssTransition(def.name || 'v'))</span><br><span class="line"> }</span><br><span class="line"> extend(res, def)</span><br><span class="line"> return res</span><br><span class="line"> } else if (typeof def === 'string') {</span><br><span class="line"> return autoCssTransition(def)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">// 通过 name 属性获取过渡 CSS 类名</span><br><span class="line">const autoCssTransition: (name: string) => Object = cached(name => {</span><br><span class="line"> return {</span><br><span class="line"> enterClass: `${name}-enter`,</span><br><span class="line"> enterToClass: `${name}-enter-to`,</span><br><span class="line"> enterActiveClass: `${name}-enter-active`,</span><br><span class="line"> leaveClass: `${name}-leave`,</span><br><span class="line"> leaveToClass: `${name}-leave-to`,</span><br><span class="line"> leaveActiveClass: `${name}-leave-active`</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>resolveTransition 函数合并了过渡类名和自定义过渡类名,返回最终的过渡类名。之后就是使用这些类名来实现过渡动画。<br><em>PS:从源码中可以知道自定义过渡类名要优先于 name 定义的过渡类名。</em><br>小结一下就是:<strong>Vue.js 通过 <transition> 的 props 获取自定义过渡类名,通过 <transition> 的 name 属性解析获取过渡类名,两者合并成为最终过渡类名,用以实现过渡效果。</transition></transition></strong></p><h2 id="3-JavaScript-钩子如何实现?"><a href="#3-JavaScript-钩子如何实现?" class="headerlink" title="3. JavaScript 钩子如何实现?"></a>3. JavaScript 钩子如何实现?</h2><p>从 <code>enter</code> 函数中可以知道,在特定时间点会调用指定 JavaScript 钩子函数,所以我们只需绑定好函数即可按时间点触发。像这样:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">enterHook && enterHook(el, cb)</span><br></pre></td></tr></table></figure></p><h2 id="4-transition-组件和-transition-group-标签的基本原理是什么?"><a href="#4-transition-组件和-transition-group-标签的基本原理是什么?" class="headerlink" title="4. transition 组件和 transition-group 标签的基本原理是什么?"></a>4. transition 组件和 transition-group 标签的基本原理是什么?</h2><p>其实就是 Vue.js 的组件,在其中实现了过渡效果而已。<br>transition 中只能包含一个子元素,标签通过 render 函数来渲染子元素(不渲染自身,所以我们在 DOM 中看不到 transition 节点)。主要用于控制元素的进入和离开,当元素离开后元素就从 DOM 中移除了。<br>transition-group 可以包含多个子元素,也是用 render 函数,渲染为指定标签名的元素。相比 transition 多了一个 v-move 属性用于控制多个组件间的移动速度。</p><h2 id="5-v-if、v-show、component-等组件变化如何监听?"><a href="#5-v-if、v-show、component-等组件变化如何监听?" class="headerlink" title="5. v-if、v-show、component 等组件变化如何监听?"></a>5. v-if、v-show、component 等组件变化如何监听?</h2><p>在使用 v-if、v-else 和 component 切换组件的时候,v-if、v-else 需要传入 key 以区分相同标签的不同元素。而 component 标签不需要。在代码中会解析 key 和 component 名组成新的 key,所以两个不同的 component 也会拥有不同的 key 实现切换效果。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> id = <span class="string">"__transition-"</span> + (<span class="keyword">this</span>._uid) + <span class="string">"-"</span>;</span><br><span class="line">child.key = child.key == <span class="literal">null</span></span><br><span class="line"> ? child.isComment</span><br><span class="line"> ? id + <span class="string">'comment'</span></span><br><span class="line"> : id + child.tag</span><br><span class="line"> : isPrimitive(child.key)</span><br><span class="line"> ? (<span class="built_in">String</span>(child.key).indexOf(id) === <span class="number">0</span> ? child.key : id + child.key)</span><br><span class="line"> : child.key;</span><br></pre></td></tr></table></figure></p><p>而对于 v-show,做了特殊标记 —— 当有 v-show 指令时标记 child.data.show 为 true:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (child.data.directives && child.data.directives.some(<span class="function"><span class="params">d</span> =></span> d.name === <span class="string">'show'</span>)) {</span><br><span class="line"> child.data.show = <span class="literal">true</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>之后再过渡的逻辑中对 v-show 做了些处理,实现过渡效果。<br>同时,在 <code>v-show</code> 的源码 <code>src/platforms/web/runtime/directives/show.js</code> 中对于 transition 也做了一些处理。比如在 update 方法中获取 transition,如果有过渡则 v-show 使用过渡效果,否则使用 <code>style.display</code> 来隐藏元素。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">update (el: any, { value, oldValue }: VNodeDirective, <span class="attr">vnode</span>: VNodeWithData) {</span><br><span class="line"> <span class="keyword">if</span> (value === oldValue) <span class="keyword">return</span></span><br><span class="line"> vnode = locateNode(vnode)</span><br><span class="line"> <span class="comment">// 过渡效果</span></span><br><span class="line"> <span class="keyword">const</span> transition = vnode.data && vnode.data.transition</span><br><span class="line"> <span class="keyword">if</span> (transition) {</span><br><span class="line"> vnode.data.show = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">if</span> (value) {</span><br><span class="line"> enter(vnode, () => {</span><br><span class="line"> el.style.display = el.__vOriginalDisplay</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> leave(vnode, () => {</span><br><span class="line"> el.style.display = <span class="string">'none'</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 隐藏</span></span><br><span class="line"> el.style.display = value ? el.__vOriginalDisplay : <span class="string">'none'</span></span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><h2 id="6-transition-中两个相同标签的组件为何要用-key-分开?"><a href="#6-transition-中两个相同标签的组件为何要用-key-分开?" class="headerlink" title="6. transition 中两个相同标签的组件为何要用 key 分开?"></a>6. transition 中两个相同标签的组件为何要用 key 分开?</h2><p>使用 key 和 tagName 来判断是否为同一个节点。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isSameChild</span> (<span class="params">child: VNode, oldChild: VNode</span>): <span class="title">boolean</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> oldChild.key === child.key && oldChild.tag === child.tag</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="8-过渡逻辑和过渡组件如何作用于一起"><a href="#8-过渡逻辑和过渡组件如何作用于一起" class="headerlink" title="8. 过渡逻辑和过渡组件如何作用于一起"></a>8. 过渡逻辑和过渡组件如何作用于一起</h2><p>在源码中有四个过渡相关的源码:</p><ul><li><code>src/platforms/web/runtime/components/transition.js</code> <transition> 组件源码。</transition></li><li><code>src/platforms/web/runtime/components/transition-group.js</code> <transition-group> 组件源码</transition-group></li><li><code>src/platforms/web/runtime/transition-util.js</code> 过渡工具代码。</li><li><code>src/platforms/web/runtime/modules/transition.js</code> 过渡逻辑代码。</li></ul><p>前三个很好理解,最后一个 transition.js 其实是在 patch 方法中和 v-show 中使用的~<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/platforms/web/runtime/directives/show.js</span></span><br><span class="line"><span class="keyword">import</span> { enter, leave } <span class="keyword">from</span> <span class="string">'../modules/transition'</span></span><br></pre></td></tr></table></figure></p><p>v-show 中调用了 transition 的 enter 和 leave 函数,在 v-show 作用于过渡效果时调用。<br>另外一个使用的地方比较隐蔽,先来看看 transition.js 导出的内容:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/platforms/web/runtime/modules/transition.js</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">_enter</span> (<span class="params">_: any, vnode: VNodeWithData</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (vnode.data.show !== <span class="literal">true</span>) {</span><br><span class="line"> enter(vnode)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> inBrowser ? {</span><br><span class="line"> create: _enter,</span><br><span class="line"> activate: _enter,</span><br><span class="line"> remove (vnode: VNode, <span class="attr">rm</span>: <span class="built_in">Function</span>) {</span><br><span class="line"> <span class="comment">/* istanbul ignore else */</span></span><br><span class="line"> <span class="keyword">if</span> (vnode.data.show !== <span class="literal">true</span>) {</span><br><span class="line"> leave(vnode, rm)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> rm()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">} : {}</span><br></pre></td></tr></table></figure></p><p>这里讲 enter 和 leave 函数在方法中使用并导出(如果是浏览器的话)。继续往下找:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/platforms/web/runtime/modules/index.js</span></span><br><span class="line"><span class="keyword">import</span> transition <span class="keyword">from</span> <span class="string">'./transition'</span></span><br></pre></td></tr></table></figure></p><p>导入到 modules 文件夹 index.js,index.js 在 patch.js 中使用了。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// src/platforms/web/runtime/patch.js</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> nodeOps <span class="keyword">from</span> <span class="string">'web/runtime/node-ops'</span></span><br><span class="line"><span class="keyword">import</span> { createPatchFunction } <span class="keyword">from</span> <span class="string">'core/vdom/patch'</span></span><br><span class="line"><span class="keyword">import</span> baseModules <span class="keyword">from</span> <span class="string">'core/vdom/modules/index'</span></span><br><span class="line"><span class="keyword">import</span> platformModules <span class="keyword">from</span> <span class="string">'web/runtime/modules/index'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> modules = platformModules.concat(baseModules)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> patch: <span class="built_in">Function</span> = createPatchFunction({ nodeOps, modules })</span><br></pre></td></tr></table></figure></p><p>在此处合并 modules,并且创建了 patch 方法。这个 patch 方法在之前写的<a href="https://www.jianshu.com/p/9db8eb16d76f" target="_blank" rel="noopener">Vue.js 源码学习六 —— VNode虚拟DOM学习</a>中提到过,用于对比虚拟 DOM,实现差异化更新。<br>可以看下 modules 在 createPatchFunction 方法中做了些什么?<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> hooks = [<span class="string">'create'</span>, <span class="string">'activate'</span>, <span class="string">'update'</span>, <span class="string">'remove'</span>, <span class="string">'destroy'</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">createPatchFunction</span> (<span class="params">backend</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> i, j</span><br><span class="line"> <span class="keyword">const</span> cbs = {}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> { modules, nodeOps } = backend</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (i = <span class="number">0</span>; i < hooks.length; ++i) {</span><br><span class="line"> cbs[hooks[i]] = []</span><br><span class="line"> <span class="keyword">for</span> (j = <span class="number">0</span>; j < modules.length; ++j) {</span><br><span class="line"> <span class="keyword">if</span> (isDef(modules[j][hooks[i]])) {</span><br><span class="line"> cbs[hooks[i]].push(modules[j][hooks[i]])</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>这里可以发现 transition.js 中导出的 create、activate 和 remove 方法都是 patch 的生命周期函数。也就是说当元素创建、激活、移除行为时就会执行 transition.js 中的逻辑,而 <transition> 和 <transition-group> 组件都会有组件的这些行为。Vue.js 很巧妙的将组件相关行为都交给了 patch 的生命周期去处理,学习了!</transition-group></transition></p><h2 id="8-过渡模式-mode-的实现原理是啥"><a href="#8-过渡模式-mode-的实现原理是啥" class="headerlink" title="8. 过渡模式 mode 的实现原理是啥"></a>8. 过渡模式 mode 的实现原理是啥</h2><p>在 <transition> 组件的 render 函数中有这么一段:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 控制离开/进入的过渡时间序列。有效的模式有 "out-in" 和 "in-out";默认同时生效。</span></span><br><span class="line"><span class="keyword">if</span> (mode === <span class="string">'out-in'</span>) {</span><br><span class="line"> <span class="comment">// return placeholder node and queue update when leave finishes</span></span><br><span class="line"> <span class="keyword">this</span>._leaving = <span class="literal">true</span></span><br><span class="line"> mergeVNodeHook(oldData, <span class="string">'afterLeave'</span>, () => {</span><br><span class="line"> <span class="keyword">this</span>._leaving = <span class="literal">false</span></span><br><span class="line"> <span class="keyword">this</span>.$forceUpdate()</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> placeholder(h, rawChild)</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (mode === <span class="string">'in-out'</span>) {</span><br><span class="line"> <span class="keyword">if</span> (isAsyncPlaceholder(child)) {</span><br><span class="line"> <span class="keyword">return</span> oldRawChild</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> delayedLeave</span><br><span class="line"> <span class="keyword">const</span> performLeave = <span class="function"><span class="params">()</span> =></span> { delayedLeave() }</span><br><span class="line"> mergeVNodeHook(data, <span class="string">'afterEnter'</span>, performLeave)</span><br><span class="line"> mergeVNodeHook(data, <span class="string">'enterCancelled'</span>, performLeave)</span><br><span class="line"> mergeVNodeHook(oldData, <span class="string">'delayLeave'</span>, leave => { delayedLeave = leave })</span><br><span class="line">}</span><br></pre></td></tr></table></figure></transition></p><p>这里就是 mode 的实现代码了,先看看两种 mode 的用法</p><blockquote><ul><li>in-out:新元素先进行过渡,完成之后当前元素过渡离开。</li><li>out-in:当前元素先进行过渡,完成之后新元素过渡进入。</li></ul></blockquote><p>可以看到,在 out-in 逻辑中,当切换元素时,先不渲染第二个组件而是返回,之后才会返回 placeholder 函数结果,当第一个元素完全 leave 后加载第二个元素。而在 in-out 元素中做的是将第一个元素延时到第二个元素 enter 后再 leave。</p><h2 id="9-的-v-move-重新排序一组内容,如何实现的移动变化?"><a href="#9-的-v-move-重新排序一组内容,如何实现的移动变化?" class="headerlink" title="9. 的 v-move 重新排序一组内容,如何实现的移动变化?"></a>9. <transition-group> 的 v-move 重新排序一组内容,如何实现的移动变化?</transition-group></h2><p>比如我们有一个 1-5 的数组使用 v-for 遍历显示到 transition-group 中,当数组发生变化时,会做如下操作:</p><ul><li>初始数组 <code>[ 1, 2, 3, 4, 5 ]</code></li><li>数组发生变化 <code>[ 1, 4, 3, 2, 5 ]</code></li><li>在 render 函数中记录变化前后额数组 preChildren 和 children 两个 VNode 数组。</li><li>在 render 函数中使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect" target="_blank" rel="noopener">getBoundingClientRect()</a> 方法记录变化前每个元素的位置 oldPos。</li><li>获取要保留和移除的元素数组。</li><li>渲染变化后的数组元素。</li><li>在 beforeUpdate 方法中使用 patch 方法移除要移除的元素。</li><li>进入 Updated 方法中,注意此时渲染结果已经是新数组 <code>[ 1, 4, 3, 2, 5 ]</code> 了。</li><li>获取过渡类名和子元素数组 children。</li><li>遍历调用<ul><li>执行回调函数</li><li>计算当前各元素位置 newPos</li><li>根据 oldPos 和 newPos,使用内联样式 <code>translate(${dx}px,${dy}px)</code> 将元素移动到之前的位置,看着就像是 <code>[ 1, 2, 3, 4, 5 ]</code></li></ul></li><li>最后遍历元素,添加 moveClass 类名,移除 <code>translate(${dx}px,${dy}px)</code> 内联样式。绑定 <a href="https://developer.mozilla.org/en-US/docs/Web/Events/transitionend" target="_blank" rel="noopener">transitionend</a> 事件。</li></ul><p>代码太长,就不多贴了~可以<strong><a href="https://github.com/violetjack/VueStudyDemos/blob/master/VueCodes/vue/src/platforms/web/runtime/components/transition-group.js" target="_blank" rel="noopener">点击这里</a></strong>跳转查看。<strong>总结下来就是先改变元素,然后把元素移动成之前的样子,然后使用过渡类名定义过渡时间实现过渡效果。</strong><br>v-move 的关键就是“假装元素位置没变”的行为。让我们看上去像是慢慢移动的。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">applyTranslation</span> (<span class="params">c: VNode</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> oldPos = c.data.pos</span><br><span class="line"> <span class="keyword">const</span> newPos = c.data.newPos</span><br><span class="line"> <span class="keyword">const</span> dx = oldPos.left - newPos.left</span><br><span class="line"> <span class="keyword">const</span> dy = oldPos.top - newPos.top</span><br><span class="line"> <span class="keyword">if</span> (dx || dy) {</span><br><span class="line"> <span class="comment">// 定义 0 秒的 translate 内联样式把元素移动到原来的样子</span></span><br><span class="line"> c.data.moved = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">const</span> s = c.elm.style</span><br><span class="line"> s.transform = s.WebkitTransform = <span class="string">`translate(<span class="subst">${dx}</span>px,<span class="subst">${dy}</span>px)`</span></span><br><span class="line"> s.transitionDuration = <span class="string">'0s'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="10-vue-的-transition-和-CSS3-的-transition-有何不同?"><a href="#10-vue-的-transition-和-CSS3-的-transition-有何不同?" class="headerlink" title="10. vue 的 transition 和 CSS3 的 transition 有何不同?"></a>10. vue 的 transition 和 CSS3 的 transition 有何不同?</h2><p>基本原理都是使用了 CSS3 的 transition,但是 Vue 的 transition 组件是配合着 VDOM 来写的、同时提供了过渡各阶段效果的 CSS 和 JS 控制,便于我们快捷、精确、安全地实现一些简单或复杂的过渡效果。</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>原本只是想看看 transition 如何实现,却扯出这么一堆问题。其中关于 transition 和 transition-group 组件讲的有点草率,有兴趣可以再深入学习下~<br>从本次学习中我学到了:</p><ul><li>更加优雅高效的 JS 逻辑写法(patch 中的生命周期统一处理 DOM 操作中的逻辑)</li><li>更加熟悉 CSS3 的 transition 过渡属性。</li><li>解决了我对 transition 的各种疑问。</li></ul><p>OK,关于 Vue 的过渡效果就聊到这儿了,写了三天……我得去休息休息了 0.0</p>]]></content>
<summary type="html">
<blockquote>
<p>在学习 element ui 时,发现组件的过渡用的是 Vue.js 提供的 <transition> 标签。这里来好好认识下 vue 的过渡到底是如何工作的。</transition></p>
</blockquote>
<h1 id="简介">
</summary>
<category term="Vue.js源码学习" scheme="https://violetjack.github.io/tags/Vue-js%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>element 源码学习五 —— Notice 系列组件学习</title>
<link href="https://violetjack.github.io/2018/04/03/element%20%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%E4%BA%94%20%E2%80%94%E2%80%94%20Notice%20%E7%B3%BB%E5%88%97%E7%BB%84%E4%BB%B6%E5%AD%A6%E4%B9%A0/"/>
<id>https://violetjack.github.io/2018/04/03/element 源码学习五 —— Notice 系列组件学习/</id>
<published>2018-04-02T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.254Z</updated>
<content type="html"><![CDATA[<blockquote><p>消息提示行为是开发中非常常见的功能,Element 为我们提供了非常好用和美观的消息提示组件。这里就简单学习下 Notice 组件的 CSS 和代码逻辑。</p></blockquote><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>Notice 包括了五类组件:</p><ul><li><strong>Alert</strong> 用于页面中展示重要的提示信息。</li><li><strong>Loading</strong> 加载数据时显示动效。</li><li><strong>Message</strong> 常用于主动操作后的反馈提示。与 Notification 的区别是后者更多用于系统级通知的被动提醒。</li><li><strong>MessageBox</strong> 模拟系统的消息提示框而实现的一套模态对话框组件,用于消息提示、确认消息和提交内容。</li><li><strong>Notification</strong> 悬浮出现在页面角落,显示全局的通知提醒消息。</li></ul><p>本文中不同角度来学习这些组件(这些组件有很多相似性,所以一起学习啦~)。</p><h1 id="demo"><a href="#demo" class="headerlink" title="demo"></a>demo</h1><p>下面是参照 element ui 写的一个小demo,尝试着了解下其中的 CSS<br>贴代码:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>My Notice<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.success-color</span> {</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background-color</span>: <span class="selector-id">#f0f9eb</span>;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">color</span>: <span class="selector-id">#67c23a</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.info-color</span> {</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background-color</span>: <span class="selector-id">#f4f4f5</span>;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">color</span>: <span class="selector-id">#909399</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.warning-color</span> {</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background-color</span>: <span class="selector-id">#fdf6ec</span>;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">color</span>: <span class="selector-id">#e6a23c</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.error-color</span> {</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background-color</span>: <span class="selector-id">#fef0f0</span>;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">color</span>: <span class="selector-id">#f56c6c</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.alert-container</span> {</span></span><br><span class="line"><span class="undefined"> position: relative;</span></span><br><span class="line"><span class="undefined"> padding: 8px 16px;</span></span><br><span class="line"><span class="undefined"> border-radius: 5px;</span></span><br><span class="line"><span class="undefined"> opacity: 1;</span></span><br><span class="line"><span class="undefined"> align-items: center;</span></span><br><span class="line"><span class="undefined"> overflow: hidden;</span></span><br><span class="line"><span class="undefined"> display: flex;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.success-text</span> {</span></span><br><span class="line"><span class="undefined"> font-size: 13px;</span></span><br><span class="line"><span class="undefined"> line-height: 18px;</span></span><br><span class="line"><span class="undefined"> padding: 0 8px;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">color</span>: <span class="selector-id">#67c23a</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.close</span> {</span></span><br><span class="line"><span class="undefined"> font-size: 13px;</span></span><br><span class="line"><span class="undefined"> position: absolute;</span></span><br><span class="line"><span class="undefined"> top: 12px;</span></span><br><span class="line"><span class="undefined"> right: 15px;</span></span><br><span class="line"><span class="undefined"> cursor: pointer;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.loading-background</span> {</span></span><br><span class="line"><span class="undefined"> position: fixed;</span></span><br><span class="line"><span class="undefined"> z-index: 2000;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background-color</span>: <span class="selector-tag">rgba</span>(26, 26, 26, 0<span class="selector-class">.9</span>);</span></span><br><span class="line"><span class="undefined"> margin: 0;</span></span><br><span class="line"><span class="undefined"> top: 0;</span></span><br><span class="line"><span class="undefined"> right: 0;</span></span><br><span class="line"><span class="undefined"> bottom: 0;</span></span><br><span class="line"><span class="undefined"> left: 0;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">transition</span>: <span class="selector-tag">opacity</span> 0<span class="selector-class">.3s</span>;</span></span><br><span class="line"><span class="undefined"> z-index: 2000;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.loading-div</span> {</span></span><br><span class="line"><span class="undefined"> top: 50%;</span></span><br><span class="line"><span class="undefined"> position: absolute;</span></span><br><span class="line"><span class="undefined"> margin-top: -20px;</span></span><br><span class="line"><span class="undefined"> height: 40px;</span></span><br><span class="line"><span class="undefined"> width: 100%;</span></span><br><span class="line"><span class="undefined"> align-items: center;</span></span><br><span class="line"><span class="undefined"> justify-content: center;</span></span><br><span class="line"><span class="undefined"> display: flex;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.loading-text</span> {</span></span><br><span class="line"><span class="css"> <span class="selector-tag">color</span>: <span class="selector-id">#f0f9eb</span>;</span></span><br><span class="line"><span class="undefined"> font-size: 15px;</span></span><br><span class="line"><span class="undefined"> overflow: hidden;</span></span><br><span class="line"><span class="undefined"> z-index: 2001;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.el-message</span> {</span></span><br><span class="line"><span class="undefined"> min-width: 380px;</span></span><br><span class="line"><span class="undefined"> box-sizing: border-box;</span></span><br><span class="line"><span class="undefined"> border-radius: 4px;</span></span><br><span class="line"><span class="undefined"> border-width: 1px;</span></span><br><span class="line"><span class="undefined"> border-style: solid;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">border-color</span>: <span class="selector-id">#ebeef5</span>;</span></span><br><span class="line"><span class="undefined"> position: fixed;</span></span><br><span class="line"><span class="undefined"> left: 50%;</span></span><br><span class="line"><span class="undefined"> top: 20px;</span></span><br><span class="line"><span class="undefined"> transform: translateX(-50%);</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background-color</span>: <span class="selector-id">#edf2fc</span>;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">transition</span>: <span class="selector-tag">opacity</span> 0<span class="selector-class">.3s</span>, <span class="selector-tag">transform</span> 0<span class="selector-class">.4s</span>;</span></span><br><span class="line"><span class="undefined"> overflow: hidden;</span></span><br><span class="line"><span class="undefined"> padding: 15px 15px 15px 20px;</span></span><br><span class="line"><span class="undefined"> display: flex;</span></span><br><span class="line"><span class="undefined"> align-items: center;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"alert-container success-color"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"success-text"</span>></span>成功提示的文案<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"close"</span>></span>X<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">id</span>=<span class="string">"showLoading"</span>></span>显示加载中<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">id</span>=<span class="string">"showMessage"</span>></span>显示信息<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"el-message"</span> <span class="attr">id</span>=<span class="string">"message"</span> <span class="attr">style</span>=<span class="string">"display:none;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一条消息<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> alertContainer = <span class="built_in">document</span>.getElementsByClassName(<span class="string">"alert-container"</span>)[<span class="number">0</span>]</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> close = <span class="built_in">document</span>.getElementsByClassName(<span class="string">"close"</span>)[<span class="number">0</span>]</span></span><br><span class="line"><span class="javascript"> close.onclick = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> alertContainer.style = <span class="string">"display:none;"</span></span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> app = <span class="built_in">document</span>.getElementById(<span class="string">"app"</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> bg = <span class="built_in">document</span>.createElement(<span class="string">"div"</span>)</span></span><br><span class="line"><span class="javascript"> bg.className = <span class="string">"loading-background"</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> div = <span class="built_in">document</span>.createElement(<span class="string">"div"</span>)</span></span><br><span class="line"><span class="javascript"> div.className = <span class="string">"loading-div"</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> text = <span class="built_in">document</span>.createElement(<span class="string">"span"</span>)</span></span><br><span class="line"><span class="javascript"> text.className = <span class="string">"loading-text"</span></span></span><br><span class="line"><span class="javascript"> text.textContent = <span class="string">"加载中……"</span></span></span><br><span class="line"><span class="undefined"> div.appendChild(text)</span></span><br><span class="line"><span class="undefined"> bg.appendChild(div)</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">"showLoading"</span>).onclick = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (<span class="built_in">document</span>.getElementsByClassName(<span class="string">"loading-background"</span>).length > <span class="number">0</span>) {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> app.appendChild(bg)</span></span><br><span class="line"><span class="javascript"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (<span class="built_in">document</span>.getElementsByClassName(<span class="string">"loading-background"</span>).length > <span class="number">0</span>) {</span></span><br><span class="line"><span class="undefined"> app.removeChild(bg)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }, 3000)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">"showMessage"</span>).onclick = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">"message"</span>).style = <span class="string">"display:block;"</span></span></span><br><span class="line"><span class="javascript"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">"message"</span>).style = <span class="string">"display:none;"</span></span></span><br><span class="line"><span class="undefined"> }, 3000)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></p><p>代码的运行结果请看 <strong><a href="https://jsfiddle.net/VioletJack/h28fnfds/2/" target="_blank" rel="noopener">->这里<-</a></strong>!</p><p>以上代码实现了</p><ul><li>Alert 的样式</li><li>loading 的简单版本 </li><li>message 的无动画版本。</li></ul><p>只实现这三个功能的原因是另外两个功能和扩展功能都是基于这个demo扩展的。<br>Alert 是一个静态的文本内容,只是外部包裹了带样式的容器。可能再多个隐藏按钮和消息图标。<br>Loading 和 MessageBox 其实的基本逻辑是插入或显示新的内容,并在显示完成后消失。需要注意的就是后面要添加一层遮罩阴影,遮罩如果非全屏使用 position:absolute 而全屏则使用 position:fixed 覆盖。另外就是注意 z-index 属性将组件放到视图最上层。<br>Message 和 Notification 其实就是文本内容、图标和按钮组合容器的现实和隐藏过程。它们的过渡动画使用的是 vue 的<a href="https://cn.vuejs.org/v2/guide/transitions.html" target="_blank" rel="noopener">进入/离开 & 列表过渡</a>来实现。</p><h1 id="通过几个问题来看源码"><a href="#通过几个问题来看源码" class="headerlink" title="通过几个问题来看源码"></a>通过几个问题来看源码</h1><p>其实从样式上,上面的 demo 已经实现了大致的样子了。下面来看看组件的一些逻辑。源码内容比较多,所以就以问答的方式有目的的来看源码。</p><h2 id="Alert-的界面实现?"><a href="#Alert-的界面实现?" class="headerlink" title="Alert 的界面实现?"></a>Alert 的界面实现?</h2><p>Alert 由图标、文本内容、描述内容和关闭按钮组成:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">transition</span> <span class="attr">name</span>=<span class="string">"el-alert-fade"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"el-alert"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:class</span>=<span class="string">"[typeClass, center ? 'is-center' : '']"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-show</span>=<span class="string">"visible"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">role</span>=<span class="string">"alert"</span></span></span><br><span class="line"><span class="tag"> ></span></span><br><span class="line"> <span class="comment"><!-- 图标 --></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"el-alert__icon"</span> <span class="attr">:class</span>=<span class="string">"[ iconClass, isBigIcon ]"</span> <span class="attr">v-if</span>=<span class="string">"showIcon"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"el-alert__content"</span>></span></span><br><span class="line"> <span class="comment"><!-- 标题 --></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"el-alert__title"</span> <span class="attr">:class</span>=<span class="string">"[ isBoldTitle ]"</span> <span class="attr">v-if</span>=<span class="string">"title"</span>></span>{{ title }}<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">slot</span>></span></span><br><span class="line"> <span class="comment"><!-- 插槽,默认插入描述文本 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"el-alert__description"</span> <span class="attr">v-if</span>=<span class="string">"description"</span>></span>{{ description }}<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">slot</span>></span></span><br><span class="line"> <span class="comment"><!-- 关闭图标按钮 --></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">class</span>=<span class="string">"el-alert__closebtn"</span> <span class="attr">:class</span>=<span class="string">"{ 'is-customed': closeText !== '', 'el-icon-close': closeText === '' }"</span> <span class="attr">v-show</span>=<span class="string">"closable"</span> @<span class="attr">click</span>=<span class="string">"close()"</span>></span>{{closeText}}<span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">transition</span>></span></span><br></pre></td></tr></table></figure></p><p>组件很简单,注释上都写了~组件还做了 <code>slot</code> 插槽拓展,可以在 <code><el-alert></code> 标签内插入自定义内容。</p><h2 id="如何显示纯文本、HTML-和-VNode"><a href="#如何显示纯文本、HTML-和-VNode" class="headerlink" title="如何显示纯文本、HTML 和 VNode"></a>如何显示纯文本、HTML 和 VNode</h2><p>显示文本使用 <code></code> 指令来显示内容。<br>显示HTML使用 <code>v-html</code> 指令来渲染显示。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- 显示文本 --></span></span><br><span class="line"><span class="tag"><<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">"!dangerouslyUseHTMLString"</span> <span class="attr">class</span>=<span class="string">"el-message__content"</span>></span>{{ message }}<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"><span class="comment"><!-- 显示HTML --></span></span><br><span class="line"><span class="tag"><<span class="name">p</span> <span class="attr">v-else</span> <span class="attr">v-html</span>=<span class="string">"message"</span> <span class="attr">class</span>=<span class="string">"el-message__content"</span>></span><span class="tag"></<span class="name">p</span>></span></span><br></pre></td></tr></table></figure></p><p>使用 vue 的 render 函数生成 VNode 对象传给组件作为组件 <code>slot</code> 插槽的默认显示结果。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (isVNode(instance.message)) {</span><br><span class="line"> instance.$slots.default = [instance.message];</span><br><span class="line"> instance.message = <span class="literal">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="组件的过渡动画怎么来的"><a href="#组件的过渡动画怎么来的" class="headerlink" title="组件的过渡动画怎么来的"></a>组件的过渡动画怎么来的</h2><p>使用 Notice 系列组件时,发现组件的显示和消失都是有过渡动画的。界面看着更加友好和舒服。实现方式就是使用了 vue 的<a href="https://cn.vuejs.org/v2/guide/transitions.html" target="_blank" rel="noopener">进入/离开 & 列表过渡</a>来实现效果。</p><h2 id="界面的关闭使用的是什么方式?"><a href="#界面的关闭使用的是什么方式?" class="headerlink" title="界面的关闭使用的是什么方式?"></a>界面的关闭使用的是什么方式?</h2><p>对于 loading 和 message-box ,在没有界面时会在 body 最后添加组件内容,显示过后使用 <code>v-show="false"</code>(<code>display:none;</code>) 隐藏,随后就调用显示和隐藏界面。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 将 message-box 组件加入到 body 中</span></span><br><span class="line"><span class="built_in">document</span>.body.appendChild(instance.$el);</span><br></pre></td></tr></table></figure></p><p>对于 Alert 由于一开始就显示,只是删除按钮,所以只需修改 <code>v-show</code> 属性隐藏即可。<br>对于 message 和 notification,这两个组件可以多次弹出,逐个关闭(自动或手动)。所以这两个组件是保存在一个数组中,然后进行渲染的,关闭某个组件就是一个将组件从组件数组中移除的过程。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// id 组件id</span></span><br><span class="line"><span class="comment">// useOnClose 自定义关闭函数</span></span><br><span class="line">Message.close = <span class="function"><span class="keyword">function</span>(<span class="params">id, userOnClose</span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, len = instances.length; i < len; i++) {</span><br><span class="line"> <span class="keyword">if</span> (id === instances[i].id) {</span><br><span class="line"> <span class="comment">// 找到组件,执行自定义关闭函数并从数组中移除</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> userOnClose === <span class="string">'function'</span>) {</span><br><span class="line"> userOnClose(instances[i]);</span><br><span class="line"> }</span><br><span class="line"> instances.splice(i, <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><h2 id="message-box-的回调函数实现"><a href="#message-box-的回调函数实现" class="headerlink" title="message-box 的回调函数实现"></a>message-box 的回调函数实现</h2><p>对于 message-box 有两种函数回调:callback 函数和 Promise 函数。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="built_in">Promise</span> !== <span class="string">'undefined'</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"></span><br><span class="line"> msgQueue.push({</span><br><span class="line"> options: merge({}, defaults, MessageBox.defaults, options),</span><br><span class="line"> callback: callback,</span><br><span class="line"> resolve: resolve,</span><br><span class="line"> reject: reject</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> showNextMsg();</span><br><span class="line"> });</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> msgQueue.push({</span><br><span class="line"> options: merge({}, defaults, MessageBox.defaults, options),</span><br><span class="line"> callback: callback</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> showNextMsg();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>当然,到这一步只是将组件配置和回调函数组成对象,并没有执行。执行实在 <code>showNextMsg</code> 方法中。<code>showNextMsg</code> 方法用于组合当前组件 <code>options</code> 并写入到 DOM 中,然后显示组件。其中有这么一段关于回调的:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果 currentMsg.options.callback 为 undefined</span></span><br><span class="line"><span class="keyword">if</span> (options.callback === <span class="literal">undefined</span>) {</span><br><span class="line"> instance.callback = defaultCallback;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>所以再看看 <code>defaultCallback</code> 函数对象:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> defaultCallback = <span class="function"><span class="params">action</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (currentMsg) {</span><br><span class="line"> <span class="keyword">let</span> callback = currentMsg.callback;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> callback === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">if</span> (instance.showInput) {</span><br><span class="line"> callback(instance.inputValue, action);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> callback(action);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (currentMsg.resolve) {</span><br><span class="line"> <span class="keyword">if</span> (action === <span class="string">'confirm'</span>) {</span><br><span class="line"> <span class="keyword">if</span> (instance.showInput) {</span><br><span class="line"> currentMsg.resolve({ <span class="attr">value</span>: instance.inputValue, action });</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> currentMsg.resolve(action);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (action === <span class="string">'cancel'</span> && currentMsg.reject) {</span><br><span class="line"> currentMsg.reject(action);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>这里就可以看到回调函数和 Promise 的调用和传参过程。当组件“关闭”的时候执行 callback 方法回调:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">doClose() {</span><br><span class="line"> ……</span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.action) <span class="keyword">this</span>.callback(<span class="keyword">this</span>.action, <span class="keyword">this</span>);</span><br><span class="line"> });</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>至此实现了回调及其传参。</p><h2 id="如何实现显示多个-Notification-向下偏移插入?"><a href="#如何实现显示多个-Notification-向下偏移插入?" class="headerlink" title="如何实现显示多个 Notification 向下偏移插入?"></a>如何实现显示多个 Notification 向下偏移插入?</h2><p>偏移量计算在 Notification 的构造方法中计算获得当前组件 <code>verticalOffset</code>。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Notification = <span class="function"><span class="keyword">function</span>(<span class="params">options</span>) </span>{</span><br><span class="line"> <span class="comment">// 服务器渲染</span></span><br><span class="line"> <span class="keyword">if</span> (Vue.prototype.$isServer) <span class="keyword">return</span>;</span><br><span class="line"> options = options || {};</span><br><span class="line"> <span class="keyword">const</span> userOnClose = options.onClose; <span class="comment">// 自定义关闭</span></span><br><span class="line"> <span class="keyword">const</span> id = <span class="string">'notification_'</span> + seed++; <span class="comment">// 组件 id</span></span><br><span class="line"> <span class="keyword">const</span> position = options.position || <span class="string">'top-right'</span>; <span class="comment">// 位置</span></span><br><span class="line"> <span class="comment">// 关闭事件</span></span><br><span class="line"> options.onClose = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> Notification.close(id, userOnClose);</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 组件实例</span></span><br><span class="line"> instance = <span class="keyword">new</span> NotificationConstructor({</span><br><span class="line"> data: options</span><br><span class="line"> });</span><br><span class="line"> <span class="comment">// vnode</span></span><br><span class="line"> <span class="keyword">if</span> (isVNode(options.message)) {</span><br><span class="line"> instance.$slots.default = [options.message];</span><br><span class="line"> options.message = <span class="string">'REPLACED_BY_VNODE'</span>;</span><br><span class="line"> }</span><br><span class="line"> instance.id = id;</span><br><span class="line"> instance.vm = instance.$mount();</span><br><span class="line"> <span class="comment">// 添加实例</span></span><br><span class="line"> <span class="built_in">document</span>.body.appendChild(instance.vm.$el);</span><br><span class="line"> instance.vm.visible = <span class="literal">true</span>;</span><br><span class="line"> instance.dom = instance.vm.$el;</span><br><span class="line"> instance.dom.style.zIndex = PopupManager.nextZIndex();</span><br><span class="line"> <span class="comment">// 偏移量计算</span></span><br><span class="line"> <span class="keyword">let</span> verticalOffset = options.offset || <span class="number">0</span>;</span><br><span class="line"> instances.filter(<span class="function"><span class="params">item</span> =></span> item.position === position).forEach(<span class="function"><span class="params">item</span> =></span> {</span><br><span class="line"> verticalOffset += item.$el.offsetHeight + <span class="number">16</span>;</span><br><span class="line"> });</span><br><span class="line"> verticalOffset += <span class="number">16</span>;</span><br><span class="line"> instance.verticalOffset = verticalOffset;</span><br><span class="line"> <span class="comment">// 传入数组</span></span><br><span class="line"> instances.push(instance);</span><br><span class="line"> <span class="keyword">return</span> instance.vm;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>偏移量 <code>verticalOffset</code> 在组件 <code>package\notification/src/main.vue</code> 中使用。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">:style</span>=<span class="string">"positionStyle"</span> ></span><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">computed: {</span><br><span class="line"> <span class="comment">// 正则匹配 position 是 top 还是 bottom</span></span><br><span class="line"> verticalProperty() {</span><br><span class="line"> <span class="keyword">return</span> <span class="regexp">/^top-/</span>.test(<span class="keyword">this</span>.position) ? <span class="string">'top'</span> : <span class="string">'bottom'</span>;</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// 最后返回的内联样式</span></span><br><span class="line"> positionStyle() {</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> [<span class="keyword">this</span>.verticalProperty]: <span class="string">`<span class="subst">${ <span class="keyword">this</span>.verticalOffset }</span>px`</span></span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果 <code>position</code> 是 <code>bottom-left</code> 偏移量 <code>verticalOffset</code> 为 20,那么返回的内联样式就是:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">20px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>至此实现了偏移的功能。</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>这里简单学习了一下 Notice 系列组件的样式和逻辑。从中学到了:</p><ul><li>用 CSS 设计样式和动态修改样式属性。</li><li>用了许多 DOM 的操作</li><li>使用已有轮子 —— 用到了 Vue 的 directive、 transition 和 render。</li><li>组件设计方面,学到了使用数组管理多个同类组件;使用 <code><slot></code> 标签给开发者预留拓展空间,使用构造函数的方法来拓展组件逻辑并定义一些快捷方法。</li></ul><p>个人感觉学习一些成熟组件的源码能够学到不少东西,收获不少。</p>]]></content>
<summary type="html">
<blockquote>
<p>消息提示行为是开发中非常常见的功能,Element 为我们提供了非常好用和美观的消息提示组件。这里就简单学习下 Notice 组件的 CSS 和代码逻辑。</p>
</blockquote>
<h1 id="简介"><a href="#简介" cl
</summary>
</entry>
<entry>
<title>JS 进阶必备 —— 闭包、this、箭头函数的实践笔记</title>
<link href="https://violetjack.github.io/2018/04/01/JS%20%E8%BF%9B%E9%98%B6%E5%BF%85%E5%A4%87%20%E2%80%94%E2%80%94%20%E9%97%AD%E5%8C%85%E3%80%81this%E3%80%81%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0%E7%9A%84%E5%AE%9E%E8%B7%B5%E7%AC%94%E8%AE%B0/"/>
<id>https://violetjack.github.io/2018/04/01/JS 进阶必备 —— 闭包、this、箭头函数的实践笔记/</id>
<published>2018-03-31T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.251Z</updated>
<content type="html"><![CDATA[<blockquote><p>闭包、this 和箭头函数是三个常见面试题,也是 js 进阶之路上的拦路虎。这次还用实践熟悉这三个问题。</p></blockquote><h1 id="this-实践"><a href="#this-实践" class="headerlink" title="this 实践"></a>this 实践</h1><h2 id="demo"><a href="#demo" class="headerlink" title="demo"></a>demo</h2><p>写了一段代码来验证 this 的指向。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="string">'全局环境的this'</span>, <span class="keyword">this</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">test</span> (<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'方法中的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">child</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'方法中的方法的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"> }</span><br><span class="line"> child()</span><br><span class="line">}</span><br><span class="line">test()</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> name: <span class="string">"object"</span>,</span><br><span class="line"> doSth: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> oName = <span class="string">"obj function name"</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'对象中的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"> },</span><br><span class="line"> childObj: {</span><br><span class="line"> name: <span class="string">"childObj"</span>,</span><br><span class="line"> doSth: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> arrow = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"对象中箭头函数的this"</span>, <span class="keyword">this</span>)</span><br><span class="line"> }</span><br><span class="line"> arrow()</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'对象的对象中的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> doBibao: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> count = <span class="number">500</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'闭包构造方法的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'闭包返回结果的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">obj.doSth()</span><br><span class="line">obj.childObj.doSth()</span><br><span class="line"><span class="keyword">var</span> mbb = obj.doBibao()</span><br><span class="line">mbb()</span><br><span class="line"></span><br><span class="line">setTimeout(obj.doSth, <span class="number">1800</span>)</span><br><span class="line">setTimeout(obj.doSth.bind(obj), <span class="number">2000</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fun = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'匿名函数中的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line">}</span><br><span class="line">fun()</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Vue</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(options){</span><br><span class="line"> <span class="keyword">this</span>.name = <span class="string">"vue"</span></span><br><span class="line"> <span class="keyword">this</span>.type = <span class="string">"object"</span></span><br><span class="line"> <span class="keyword">this</span>.options = options</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'构造函数的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"></span><br><span class="line"> options.log()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> vm = <span class="keyword">new</span> Vue({</span><br><span class="line"> log: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'构造函数找那个传递方法的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bibao</span> (<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> count = <span class="number">101</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'闭包外的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> count++</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'闭包中的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line"> <span class="keyword">return</span> count;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> bi = bibao()</span><br><span class="line"><span class="built_in">console</span>.log(bi())</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> mArrow = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'箭头函数中的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line">}</span><br><span class="line">mArrow()</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'以下内容为异步执行'</span>)</span><br><span class="line"></span><br><span class="line">setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'延时箭头函数中的 this'</span>, <span class="keyword">this</span>)</span><br><span class="line">}, <span class="number">1000</span>)</span><br></pre></td></tr></table></figure></p><p>最后结果如图:<br><img src="https://upload-images.jianshu.io/upload_images/1987062-4c6444e7445a41e0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="console.log"></p><p>从中可以得到一些结论(以下都是非严格模式下的测试结果):</p><ul><li>全局变量的 this 指向 Window。</li><li>全局变量中的具名函数、匿名函数、闭包函数、箭头函数都指向 Window。</li><li>在对象中同步调用,this 指向当前对象。</li><li>在对象中异步调用,this 已重新指向 Window,如果需要指向对象需要使用 <code>bind()</code> 方法改变 this 指向。</li></ul><p>个人理解:this 作为上下文始终会有一个唯一指向对象。这个对象要么指向 Window 要么指向当前对象。<strong>当调用对象中方法时,方法中的 this 即指向方法。之后 this 会重新指向 Window。</strong></p><h2 id="call-amp-apply-amp-bind"><a href="#call-amp-apply-amp-bind" class="headerlink" title="call & apply & bind"></a>call & apply & bind</h2><p>说到 this 不得不说下函数的间接调用方法 apply 和 call 了。他们作用相同,唯一不同点在于 apply 方法的第二个参数接收一个参数数组。而 call 方法接收若干个参数。这两个方法的第一个参数传递的都是 this 指向。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sayHello</span>(<span class="params">p1, p2</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`hello <span class="subst">${p1}</span> and <span class="subst">${p2}</span>`</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">sayHello(<span class="string">'jack'</span>, <span class="string">'rose'</span>)</span><br><span class="line">sayHello.call(<span class="keyword">this</span>, <span class="string">'jack'</span>, <span class="string">'rose'</span>)</span><br><span class="line">sayHello.apply(<span class="keyword">this</span>, [ <span class="string">'jack'</span>, <span class="string">'rose'</span> ])</span><br></pre></td></tr></table></figure></p><p>如上代码,其实输出结果是一样的。第一种写法是一种语法糖,第二种和第三种才是真正的方法执行。可以看到它们的第一个参数为 this。<strong>所以,call 方法和 apply 方法是可以改变函数的 this 指向的。</strong><br>而我们之前提到的 bind 函数用于重新指定函数的 this 并且创建出一个新的函数的。引用 MDN 上的说法就是:</p><blockquote><p>bind() 方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。</p></blockquote><h1 id="箭头函数和一般函数的区别"><a href="#箭头函数和一般函数的区别" class="headerlink" title="箭头函数和一般函数的区别"></a>箭头函数和一般函数的区别</h1><p>箭头函数使用更加简洁的表达方式替代匿名函数而深受喜爱。然而箭头函数与一般匿名函数的不同点在于:<strong>箭头函数拥有静态的上下文环境,不会因为不同的调用而改变。</strong><br>以下是我理解的静态函数与一般函数的不同点:</p><ul><li><p>在箭头函数中,this 是静态的不可变的,所以bind、call和apply方法修改this不起作用:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> mor = {</span><br><span class="line"> name: <span class="string">"mor"</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> ar = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"箭头函数的this变化"</span>, <span class="keyword">this</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">ar() <span class="comment">// Window</span></span><br><span class="line">ar.call(mor, <span class="number">123</span>) <span class="comment">// Window 如果是匿名函数则返回 mor 对象结果</span></span><br><span class="line"><span class="keyword">var</span> newAr = ar.bind(mor)</span><br><span class="line">newAr() <span class="comment">// Window 如果是匿名函数则返回 mor 对象结果</span></span><br></pre></td></tr></table></figure></li><li><p>箭头函数拥有静态的上下文环境,不会因为不同的调用而改变。如下例子中箭头函数的 this 指向了 Window 对象。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> sex: <span class="string">"male"</span>,</span><br><span class="line"> age: <span class="number">28</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">person.log = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"01:"</span> + <span class="keyword">this</span>.sex + <span class="string">"-"</span> + <span class="keyword">this</span>.age) <span class="comment">// 指向 person 对象</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">person.log02 = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"02:"</span> + <span class="keyword">this</span>.sex + <span class="string">"-"</span> + <span class="keyword">this</span>.age) <span class="comment">// 指向 Window</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">person.log() <span class="comment">// 01:male-28</span></span><br><span class="line">person.log02() <span class="comment">// 02:undefined-undefined</span></span><br></pre></td></tr></table></figure></li></ul><h1 id="闭包函数的原理和用途"><a href="#闭包函数的原理和用途" class="headerlink" title="闭包函数的原理和用途"></a>闭包函数的原理和用途</h1><p>这里来简单实现一个闭包:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> a = <span class="number">100</span></span><br><span class="line"> <span class="keyword">var</span> b = <span class="number">50</span></span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(a + b)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> count = add()</span><br><span class="line">count() <span class="comment">// 150</span></span><br></pre></td></tr></table></figure></p><p>我对闭包的理解就是:<strong>在函数中返回函数表达式的写法。</strong></p><p>闭包的主要用途有:</p><ul><li>避免被垃圾回收机制回收方法结果和变量,使变量始终保存在内存中,实现缓存的功能。如需清空缓存需要将值变为 null。</li><li>通过闭包获取方法中的局部变量。</li></ul><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>注意,以上都是本人对于这些知识点的理解,可能会有描述不太准确的地方。如有错误还请评论指出,万分感谢。<br>这里简单记录了一下我对于 this、闭包和箭头函数的理解~更多内容可以看下我提供的参考资料。</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><ul><li><a href="https://github.com/zchen9/code/issues/1" target="_blank" rel="noopener">https://github.com/zchen9/code/issues/1</a></li><li><a href="https://segmentfault.com/a/1190000006875662" target="_blank" rel="noopener">https://segmentfault.com/a/1190000006875662</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions</a></li><li><a href="http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html" target="_blank" rel="noopener">http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures</a></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>闭包、this 和箭头函数是三个常见面试题,也是 js 进阶之路上的拦路虎。这次还用实践熟悉这三个问题。</p>
</blockquote>
<h1 id="this-实践"><a href="#this-实践" class="headerlink
</summary>
</entry>
<entry>
<title>element 源码学习四 —— color-picker 源码学习</title>
<link href="https://violetjack.github.io/2018/03/31/element-code-04/"/>
<id>https://violetjack.github.io/2018/03/31/element-code-04/</id>
<published>2018-03-30T16:00:00.000Z</published>
<updated>2018-04-03T01:50:38.969Z</updated>
<content type="html"><![CDATA[<blockquote><p>在 element ui 中最让我好奇的组件之一就是 color-picker 着色器组件。这里还是通过几个问题来学习一下如何实现着色器的。</p></blockquote><h1 id="源码地址"><a href="#源码地址" class="headerlink" title="源码地址"></a>源码地址</h1><p>在前几篇博客中说起过 element 组件都位于 <code>package</code> 目录下,那么本次学习的颜色选择器就是在 <code>package/color-picker</code> 目录中。<br>简单说下目录结构:<br><img src="https://upload-images.jianshu.io/upload_images/1987062-34971b1038d38db9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="目录结构"></p><ul><li><strong>src</strong> 源码文件夹<ul><li><strong>components</strong> 组件文件夹<ul><li><strong>alpha-slider.vue</strong> 透明度选择器</li><li><strong>hue-slider.vue</strong> 色调选择器</li><li><strong>picker-dropdown.vue</strong> 下拉界面(几个选择器的组合)</li><li><strong>sv-panel</strong> 颜色选择器</li></ul></li><li><strong>color.js</strong> 颜色处理逻辑</li><li><strong>draggable.js</strong> 选择器拖动效果逻辑</li><li><strong>main.vue</strong> color-picker 的整体界面实现。</li></ul></li><li><strong>cooking.conf.js</strong> <a href="http://cookingjs.github.io/zh-cn/index.html" target="_blank" rel="noopener">cooking</a> 配置</li><li><strong>index.js</strong> index文件,用于导出组件</li><li><strong>package.json</strong> 组件信息配置文件</li></ul><p>下面通过问答解决问题的方式来学习 color-picker 组件。</p><h1 id="回答几个源码问题"><a href="#回答几个源码问题" class="headerlink" title="回答几个源码问题"></a>回答几个源码问题</h1><h2 id="整体组件的结构是怎样的?"><a href="#整体组件的结构是怎样的?" class="headerlink" title="整体组件的结构是怎样的?"></a>整体组件的结构是怎样的?</h2><p>从整体结构来看,color-picker 的结构其实是多个组件的组合而成的。</p><ul><li><strong>显示颜色结果的 span</strong> 和<strong>选择颜色的下拉框</strong>组成整体的 color-picker 组件;</li><li>其中<strong>下拉框</strong>由以下组件组合而成;<ul><li>3个颜色选择器</li><li>1个input</li><li>1个清空button</li><li>1个确定button</li></ul></li></ul><p><img src="https://upload-images.jianshu.io/upload_images/1987062-f84f7d12052e86d8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="组件结构"></p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-c9e4c7f3c5b7a2aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="结构图"></p><h2 id="选择器的背景颜色变化是如何实现的?"><a href="#选择器的背景颜色变化是如何实现的?" class="headerlink" title="选择器的背景颜色变化是如何实现的?"></a>选择器的背景颜色变化是如何实现的?</h2><p>3 个颜色选择器都是由 CSS3 的线性渐变效果 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient" target="_blank" rel="noopener">linear-gradient()</a> 来实现的。下面是简化版~<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">style</span>></span><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.div01</span> {</span></span><br><span class="line"><span class="undefined"> width: 27px;</span></span><br><span class="line"><span class="undefined"> height: 350px;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background</span>: <span class="selector-tag">linear-gradient</span>(<span class="selector-tag">to</span> <span class="selector-tag">bottom</span>, <span class="selector-tag">red</span> 0, <span class="selector-id">#ff0</span> 17%, <span class="selector-id">#0f0</span> 33%, <span class="selector-id">#0ff</span> 50%, <span class="selector-id">#00f</span> 67%, <span class="selector-id">#f0f</span> 83%, <span class="selector-tag">red</span> 100%);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.bg-white</span> {</span></span><br><span class="line"><span class="undefined"> width: 450px;</span></span><br><span class="line"><span class="undefined"> height: 350px;</span></span><br><span class="line"><span class="undefined"> position: absolute;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background</span>: <span class="selector-tag">linear-gradient</span>(<span class="selector-tag">to</span> <span class="selector-tag">right</span>, <span class="selector-id">#fff</span>, <span class="selector-tag">rgba</span>(255, 255, 255, 0));</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.bg-black</span> {</span></span><br><span class="line"><span class="undefined"> width: 450px;</span></span><br><span class="line"><span class="undefined"> height: 350px;</span></span><br><span class="line"><span class="undefined"> position: absolute;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background</span>: <span class="selector-tag">linear-gradient</span>(<span class="selector-tag">to</span> <span class="selector-tag">top</span>, <span class="selector-id">#000</span>, <span class="selector-tag">transparent</span>);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.div02</span> {</span></span><br><span class="line"><span class="undefined"> width: 450px;</span></span><br><span class="line"><span class="undefined"> height: 350px;</span></span><br><span class="line"><span class="undefined"> position: relative;</span></span><br><span class="line"><span class="undefined"> background: rgb(213, 0, 255);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.div03</span> {</span></span><br><span class="line"><span class="undefined"> height: 27px;</span></span><br><span class="line"><span class="undefined"> width: 450px;</span></span><br><span class="line"><span class="undefined"> background: linear-gradient(to right, rgba(213, 0, 255, 0) 0%, rgb(213, 0, 255) 100%);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"div02"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"bg-white"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"bg-black"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"div01"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"div03"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></p><p>最终结果如图所示:<img src="https://upload-images.jianshu.io/upload_images/1987062-0318237c94811e07.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="显示结果"></p><p>原来看似复杂的颜色选择器知识用了几个渐变就组合出来了,CSS 真的很强大!</p><h2 id="如何计算并获取选中的色值?"><a href="#如何计算并获取选中的色值?" class="headerlink" title="如何计算并获取选中的色值?"></a>如何计算并获取选中的色值?</h2><p>颜色结果的计算逻辑都在 color.js 中了,来简单看下代码。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// hsv 转 hsl</span></span><br><span class="line"><span class="keyword">const</span> hsv2hsl = <span class="function"><span class="keyword">function</span>(<span class="params">hue, sat, val</span>) </span>{};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 是否为 1.0</span></span><br><span class="line"><span class="keyword">const</span> isOnePointZero = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>{};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 是否为百分比</span></span><br><span class="line"><span class="keyword">const</span> isPercentage = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>{};</span><br><span class="line"></span><br><span class="line"><span class="comment">// Take input from [0, n] and return it as [0, 1]</span></span><br><span class="line"><span class="keyword">const</span> bound01 = <span class="function"><span class="keyword">function</span>(<span class="params">value, max</span>) </span>{};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 十进制转十六进制</span></span><br><span class="line"><span class="keyword">const</span> INT_HEX_MAP = { <span class="number">10</span>: <span class="string">'A'</span>, <span class="number">11</span>: <span class="string">'B'</span>, <span class="number">12</span>: <span class="string">'C'</span>, <span class="number">13</span>: <span class="string">'D'</span>, <span class="number">14</span>: <span class="string">'E'</span>, <span class="number">15</span>: <span class="string">'F'</span> };</span><br><span class="line"></span><br><span class="line"><span class="comment">// 转为十六进制颜色值</span></span><br><span class="line"><span class="keyword">const</span> toHex = <span class="function"><span class="keyword">function</span>(<span class="params">{ r, g, b }</span>) </span>{};</span><br><span class="line"></span><br><span class="line"><span class="comment">// 十六进制转十进制</span></span><br><span class="line"><span class="keyword">const</span> HEX_INT_MAP = { <span class="attr">A</span>: <span class="number">10</span>, <span class="attr">B</span>: <span class="number">11</span>, <span class="attr">C</span>: <span class="number">12</span>, <span class="attr">D</span>: <span class="number">13</span>, <span class="attr">E</span>: <span class="number">14</span>, <span class="attr">F</span>: <span class="number">15</span> };</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解析十六进制</span></span><br><span class="line"><span class="keyword">const</span> parseHexChannel = <span class="function"><span class="keyword">function</span>(<span class="params">hex</span>) </span>{};</span><br><span class="line"></span><br><span class="line"><span class="comment">// hsl 转 hsv</span></span><br><span class="line"><span class="keyword">const</span> hsl2hsv = <span class="function"><span class="keyword">function</span>(<span class="params">hue, sat, light</span>) </span>{};</span><br><span class="line"></span><br><span class="line"><span class="comment">// rgb 转 hsv</span></span><br><span class="line"><span class="keyword">const</span> rgb2hsv = <span class="function"><span class="keyword">function</span>(<span class="params">r, g, b</span>) </span>{};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// hsv 转 rgb</span></span><br><span class="line"><span class="keyword">const</span> hsv2rgb = <span class="function"><span class="keyword">function</span>(<span class="params">h, s, v</span>) </span>{};</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">Color</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(options) {</span><br><span class="line"> <span class="keyword">this</span>._hue = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">this</span>._saturation = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">this</span>._value = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">this</span>._alpha = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.enableAlpha = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">this</span>.format = <span class="string">'hex'</span>;</span><br><span class="line"> <span class="keyword">this</span>.value = <span class="string">''</span>;</span><br><span class="line"></span><br><span class="line"> options = options || {};</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> option <span class="keyword">in</span> options) {</span><br><span class="line"> <span class="keyword">if</span> (options.hasOwnProperty(option)) {</span><br><span class="line"> <span class="keyword">this</span>[option] = options[option];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.doOnChange();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 设置属性值</span></span><br><span class="line"> set(prop, value) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">arguments</span>.length === <span class="number">1</span> && <span class="keyword">typeof</span> prop === <span class="string">'object'</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> p <span class="keyword">in</span> prop) {</span><br><span class="line"> <span class="keyword">if</span> (prop.hasOwnProperty(p)) {</span><br><span class="line"> <span class="keyword">this</span>.set(p, prop[p]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>[<span class="string">'_'</span> + prop] = value;</span><br><span class="line"> <span class="keyword">this</span>.doOnChange();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 获取属性值 _hue</span></span><br><span class="line"> get(prop) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>[<span class="string">'_'</span> + prop];</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 颜色值转为 rgb 返回</span></span><br><span class="line"> toRgb() {</span><br><span class="line"> <span class="keyword">return</span> hsv2rgb(<span class="keyword">this</span>._hue, <span class="keyword">this</span>._saturation, <span class="keyword">this</span>._value);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 格式化传入的值</span></span><br><span class="line"> fromString(value) {</span><br><span class="line"> <span class="keyword">if</span> (!value) {</span><br><span class="line"> <span class="keyword">this</span>._hue = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">this</span>._saturation = <span class="number">100</span>;</span><br><span class="line"> <span class="keyword">this</span>._value = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.doOnChange();</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 定义计算出结果后:赋值、改变。</span></span><br><span class="line"> <span class="keyword">const</span> fromHSV = <span class="function">(<span class="params">h, s, v</span>) =></span> {</span><br><span class="line"> <span class="keyword">this</span>._hue = <span class="built_in">Math</span>.max(<span class="number">0</span>, <span class="built_in">Math</span>.min(<span class="number">360</span>, h));</span><br><span class="line"> <span class="keyword">this</span>._saturation = <span class="built_in">Math</span>.max(<span class="number">0</span>, <span class="built_in">Math</span>.min(<span class="number">100</span>, s));</span><br><span class="line"> <span class="keyword">this</span>._value = <span class="built_in">Math</span>.max(<span class="number">0</span>, <span class="built_in">Math</span>.min(<span class="number">100</span>, v));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.doOnChange();</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 颜色变化逻辑,最后都会转为 HSV 三个值执行 fromHSV 方法 */</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 更具计算结果定义当前颜色值 value</span></span><br><span class="line"> doOnChange() {</span><br><span class="line"> <span class="keyword">const</span> { _hue, _saturation, _value, _alpha, format } = <span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.enableAlpha) {</span><br><span class="line"> <span class="keyword">switch</span> (format) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'hsl'</span>:</span><br><span class="line"> <span class="keyword">const</span> hsl = hsv2hsl(_hue, _saturation / <span class="number">100</span>, _value / <span class="number">100</span>);</span><br><span class="line"> <span class="keyword">this</span>.value = <span class="string">`hsla(<span class="subst">${ _hue }</span>, <span class="subst">${ <span class="built_in">Math</span>.round(hsl[<span class="number">1</span>] * <span class="number">100</span>) }</span>%, <span class="subst">${ <span class="built_in">Math</span>.round(hsl[<span class="number">2</span>] * <span class="number">100</span>) }</span>%, <span class="subst">${ _alpha <span class="regexp">/ 100})`;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> break;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> case 'hsv':</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> this.value = `hsva(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%, ${ _alpha /</span> <span class="number">100</span>}</span>)`</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">const</span> { r, g, b } = hsv2rgb(_hue, _saturation, _value);</span><br><span class="line"> <span class="keyword">this</span>.value = <span class="string">`rgba(<span class="subst">${r}</span>, <span class="subst">${g}</span>, <span class="subst">${b}</span>, <span class="subst">${ _alpha <span class="regexp">/ 100 })`;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> }</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> } else {</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> switch (format) {</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> case 'hsl':</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> const hsl = hsv2hsl(_hue, _saturation /</span> <span class="number">100</span>, _value <span class="regexp">/ 100);</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> this.value = `hsl(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%)`;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> break;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> case 'hsv':</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> this.value = `hsv(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%)`;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> break;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> case 'rgb':</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> const { r, g, b } = hsv2rgb(_hue, _saturation, _value);</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> this.value = `rgb(${r}, ${g}, ${b})`;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> break;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> default:</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> this.value = toHex(hsv2rgb(_hue, _saturation, _value));</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> }</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> }</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> }</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp">};</span></span></span></span><br></pre></td></tr></table></figure></p><p>其中,将工具方法和计算颜色的具体方法隐藏了,只看具体逻辑。<br>其实 color.js 主要是定义了一个 Color 类,简单说下其中一些方法的作用:</p><ul><li>set 用于设置 Color 中的变量。</li><li>get 用于获取 <code>_hue</code> <code>_saturation</code> <code>_value</code> <code>_alpha</code> 这四个值。</li><li>toRgb 方法将当前颜色的值(除了透明度)以 RGB 的形式返回。</li><li>fromString 方法将传入的颜色值解析成 HSV 格式,并赋值给 <code>_hue</code> <code>_saturation</code> <code>_value</code> 和 <code>_alpha</code>。</li><li>doOnChange 方法将会计算颜色值组成字符串传给 <code>value</code>。</li></ul><p>至此,Color 的大致功能就清晰了:<strong>解析传入的颜色值为 HSVA 格式分别表示为 <code>_hue</code> <code>_saturation</code> <code>_value</code> 和 <code>_alpha</code>,并且组合成颜色字符串传给 <code>value</code>。</strong><br>现在需要把获取到的颜色值传给显示结果的 span,那么就从 <code>main.vue</code> 的中显示颜色结果的 <code><span></code> 标签开始看起。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"el-color-picker__color-inner"</span> <span class="attr">:style</span>=<span class="string">"{ backgroundColor: displayedColor }"</span>></span><span class="tag"></<span class="name">span</span>></span></span><br></pre></td></tr></table></figure></p><p>背景色调用了 displayedColor 这个 computed 属性:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"> computed: {</span><br><span class="line"> displayedColor() {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.value && !<span class="keyword">this</span>.showPanelColor) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'transparent'</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">const</span> { r, g, b } = <span class="keyword">this</span>.color.toRgb();</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.showAlpha</span><br><span class="line"> ? <span class="string">`rgba(<span class="subst">${ r }</span>, <span class="subst">${ g }</span>, <span class="subst">${ b }</span>, <span class="subst">${ <span class="keyword">this</span>.color.get(<span class="string">'alpha'</span>) <span class="regexp">/ 100 })`</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> : `rgb(${ r }, ${ g }, ${ b })`;</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> }</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp"> },</span></span></span></span><br><span class="line"><span class="string"><span class="subst"><span class="regexp">}</span></span></span></span><br></pre></td></tr></table></figure></p><p>这里的 <code>this.value</code> 是 props 中传入的属性。如果没有传入 <code>value</code> 并且没有选择过颜色,那么显示透明色;<br>而 <code>this.color</code> 是 Color 类的实例化对象:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> color = <span class="keyword">new</span> Color({</span><br><span class="line"> enableAlpha: <span class="keyword">this</span>.showAlpha,</span><br><span class="line"> format: <span class="keyword">this</span>.colorFormat</span><br><span class="line"> });</span><br></pre></td></tr></table></figure></p><p>所以,就调用了我们上面所说的 <code>toRgb</code> 方法,最后返回颜色结果。<br>至此,实现了颜色的计算、获取和显示。</p><h2 id="颜色选择器如何获取和修改颜色值?"><a href="#颜色选择器如何获取和修改颜色值?" class="headerlink" title="颜色选择器如何获取和修改颜色值?"></a>颜色选择器如何获取和修改颜色值?</h2><p>在下拉菜单中 <code>hue-slider</code> 组件获取色调(哪种颜色)、<code>sv-panel</code> 获取具体的颜色值、<code>alpha-silder</code> 获取透明度。<br>这三个组件通过 props 获取父级组件传递的的 <code>color</code> 对象来显示颜色。如果颜色选择器的选择块移动后,通过修改 <code>color</code> 值来实现颜色的修改。</p><h2 id="颜色选择器的选择块如何实现"><a href="#颜色选择器的选择块如何实现" class="headerlink" title="颜色选择器的选择块如何实现"></a>颜色选择器的选择块如何实现</h2><p>选择颜色的过程其实就是选择器位移发生变化的过程。下面是作者参照 element 做的一个在有限范围内任意移动选择器的 demo:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">style</span>></span><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-id">#container</span> {</span></span><br><span class="line"><span class="undefined"> width: 500px;</span></span><br><span class="line"><span class="undefined"> height: 500px;</span></span><br><span class="line"><span class="undefined"> position: relative;</span></span><br><span class="line"><span class="undefined"> border: 1px solid black;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.drag</span> {</span></span><br><span class="line"><span class="undefined"> height: 4px;</span></span><br><span class="line"><span class="undefined"> width: 4px;</span></span><br><span class="line"><span class="undefined"> position: absolute;</span></span><br><span class="line"><span class="undefined"> border-radius: 50%;</span></span><br><span class="line"><span class="undefined"> border: 1px solid red;</span></span><br><span class="line"><span class="undefined"> cursor: pointer;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"container"</span> <span class="attr">ref</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"drag"</span> </span></span><br><span class="line"><span class="tag"> <span class="attr">:style</span>=<span class="string">"{</span></span></span><br><span class="line"><span class="tag"><span class="string"> top: cursorTop + 'px',</span></span></span><br><span class="line"><span class="tag"><span class="string"> left: cursorLeft + 'px'</span></span></span><br><span class="line"><span class="tag"><span class="string"> }"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">new</span> Vue({</span></span><br><span class="line"><span class="javascript"> el: <span class="string">"#app"</span>,</span></span><br><span class="line"><span class="undefined"> data: {</span></span><br><span class="line"><span class="undefined"> cursorLeft: 0,</span></span><br><span class="line"><span class="undefined"> cursorTop: 0,</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="undefined"> mounted() {</span></span><br><span class="line"><span class="javascript"> draggable(<span class="keyword">this</span>.$el, {</span></span><br><span class="line"><span class="javascript"> drag: <span class="function">(<span class="params">event</span>) =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.handleDrag(event);</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> end: <span class="function">(<span class="params">event</span>) =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.handleDrag(event);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> });</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.update();</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="undefined"> methods: {</span></span><br><span class="line"><span class="undefined"> handleDrag(event) {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> container = <span class="keyword">this</span>.$refs.container</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> el = <span class="keyword">this</span>.$el;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> rect = container.getBoundingClientRect();</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> left = event.clientX - rect.left;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> top = event.clientY - rect.top;</span></span><br><span class="line"><span class="javascript"> left = <span class="built_in">Math</span>.max(<span class="number">0</span>, left);</span></span><br><span class="line"><span class="javascript"> left = <span class="built_in">Math</span>.min(left, rect.width - <span class="number">6</span>);</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> top = <span class="built_in">Math</span>.max(<span class="number">0</span>, top);</span></span><br><span class="line"><span class="javascript"> top = <span class="built_in">Math</span>.min(top, rect.height - <span class="number">6</span>);</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.cursorLeft = left;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.cursorTop = top;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> })</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> isDragging = <span class="literal">false</span>;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">draggable</span>(<span class="params">element, options</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (Vue.prototype.$isServer) <span class="keyword">return</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> moveFn = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (options.drag) {</span></span><br><span class="line"><span class="undefined"> options.drag(event);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> };</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> upFn = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.removeEventListener(<span class="string">'mousemove'</span>, moveFn);</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.removeEventListener(<span class="string">'mouseup'</span>, upFn);</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.onselectstart = <span class="literal">null</span>;</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.ondragstart = <span class="literal">null</span>;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> isDragging = <span class="literal">false</span>;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (options.end) {</span></span><br><span class="line"><span class="undefined"> options.end(event);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> };</span></span><br><span class="line"><span class="javascript"> element.addEventListener(<span class="string">'mousedown'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (isDragging) <span class="keyword">return</span>;</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.onselectstart = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ <span class="keyword">return</span> <span class="literal">false</span>; };</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.ondragstart = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ <span class="keyword">return</span> <span class="literal">false</span>; };</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.addEventListener(<span class="string">'mousemove'</span>, moveFn);</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.addEventListener(<span class="string">'mouseup'</span>, upFn);</span></span><br><span class="line"><span class="javascript"> isDragging = <span class="literal">true</span>;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (options.start) {</span></span><br><span class="line"><span class="undefined"> options.start(event);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> });</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure></p><p>好吧,我知道代码太长了,要看效果请移步<a href="https://jsfiddle.net/VioletJack/ezttuvmf/1/" target="_blank" rel="noopener">此处</a>。<br>选择器的逻辑如下:</p><ul><li>根据 props 传入的颜色值初次计算选择器的位置。</li><li>拖动选择器,根据选择器位置、已知的 color 属性计算当前选择器位置的颜色结果。</li></ul><p>也就是说做一个选择器需要的就是<strong>一个可拖动的选择器</strong>和<strong>一套计算颜色的算法逻辑</strong>。比如在 <code>sv-silder</code> 中的算法逻辑如下:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 计算 cursor 位置</span></span><br><span class="line"><span class="keyword">this</span>.cursorLeft = saturation * width / <span class="number">100</span>;</span><br><span class="line"><span class="keyword">this</span>.cursorTop = (<span class="number">100</span> - value) * height / <span class="number">100</span>;</span><br><span class="line"><span class="comment">// 计算颜色</span></span><br><span class="line"><span class="keyword">this</span>.color.set({</span><br><span class="line"> saturation: left / rect.width * <span class="number">100</span>,</span><br><span class="line"> value: <span class="number">100</span> - top / rect.height * <span class="number">100</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure></p><p>其他两个选择器原理也是类似的~</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>至此,我对 color-picker 的一些疑惑都解开了,也写了一些 demo 来玩玩。对该组件有了大致的理解了~不得不感叹作者对于 CSS 和 Vue 的掌握真的非常熟练。学到了不少东西,感谢开源社区给我们提供了那么多好东西给我们使用和学习~<br>再下一篇文章中我想探索下其他一些有趣的 element 组件,敬请期待!</p>]]></content>
<summary type="html">
<blockquote>
<p>在 element ui 中最让我好奇的组件之一就是 color-picker 着色器组件。这里还是通过几个问题来学习一下如何实现着色器的。</p>
</blockquote>
<h1 id="源码地址"><a href="#源码地址" class
</summary>
<category term="element源码学习" scheme="https://violetjack.github.io/tags/element%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>CSS 基础知识整理</title>
<link href="https://violetjack.github.io/2018/03/28/CSS%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86/"/>
<id>https://violetjack.github.io/2018/03/28/CSS 基础知识整理/</id>
<published>2018-03-27T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.250Z</updated>
<content type="html"><![CDATA[<blockquote><p>对于 CSS,由于过度依赖美工导致 CSS 很弱,所以这次好好学习 CSS相关东西。</p></blockquote><h1 id="盒子模型"><a href="#盒子模型" class="headerlink" title="盒子模型"></a>盒子模型</h1><p>盒子模型是由 margin、border、padding 和 content 属性组成的。</p><ul><li>margin 就是 border 外的透明边距区域。如 <code>margin: 10px;</code>。</li><li>border 就是边框属性,可以定义边框的宽度、样式和颜色。如 <code>border: 5px solid red</code>。</li><li>padding 就是 border 内的透明填充边距。如 <code>padding: 10px;</code>。</li><li>content 也就是容器内要显示内容的区域。</li></ul><p>盒子模型唯一要注意的就是边框的界定问题。IE 的 width 和 height 包括了 border 和 padding 的宽度,而 w3c (其他浏览器)的 wdith 和 height 只作用域 content,与 padding、padding 都区分出来的。<br>即在 IE 中,<code>content.width + padding.width * 2 + border.width * 2 = width</code>。<br>w3c 中 <code>content.width = width</code>。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span> {</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">2px</span> solid red;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">30px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">60px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://upload-images.jianshu.io/upload_images/1987062-41823df9d5d2ab83.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="chrome中的盒子模型"></p><h1 id="overflow"><a href="#overflow" class="headerlink" title="overflow"></a>overflow</h1><ul><li>visible 超出边框显示</li><li>hidden 隐藏超出文本</li><li>scroll 换行滚动</li><li>auto 自动换行</li><li>inherit 继承父级CSS的属性</li></ul><p>关于 scroll 和 auto 的区别,这篇<a href="https://www.jianshu.com/p/1ac2933891cd" target="_blank" rel="noopener">文章</a>中讲的很详细。</p><h1 id="display"><a href="#display" class="headerlink" title="display"></a>display</h1><ul><li>none 隐藏,不占空间</li><li>block 元素间换行,带有换行符</li><li>inline 默认,元素间不换行。宽度满了之后换行显示,流式布局显示。</li><li>inline-block 和 inline 效果相同,流式布局显示。于 inline 不同之处在于 inline 不可设置宽高,inline-block 可以设置宽高。</li><li>list-item 作为列表元素,换行显示。</li><li>table 块级元素表格,后面带有回车。与 block 和 list-item 不同之处于宽度不是撑满整个页面。</li><li>flex 将当前容器元素变为 flex 弹性盒模型。</li></ul><p>其他 display 还有许多 table-<em>*</em> 属性,感觉不常用暂且忽略了。</p><h1 id="position"><a href="#position" class="headerlink" title="position"></a>position</h1><ul><li>absolute 对非 static 父元素的绝对定位,脱离流。</li><li>fixed 对于浏览器窗口的绝对定位,悬浮,脱离流。</li><li>relative 相对父元素定位,在流中。</li><li>static 默认值,没有定位,在流中。</li></ul><h1 id="left、right、top、bottom"><a href="#left、right、top、bottom" class="headerlink" title="left、right、top、bottom"></a>left、right、top、bottom</h1><p>这四个属性定义了<strong>定位元素相应外边距边界</strong>与<strong>其包含块相应边界</strong>之间的偏移。如 <code>left: 20;px</code> 就是子元素相对于父元素向右偏移移 20px。<br>如果元素定义为 absolute,不设置 width 和 height,而设置上下左右四个属性那么就会根据父级元素容器来定义元素的宽高。<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.child</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">15px</span>;</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">25px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: green;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>如果同时设置了 <code>width</code> <code>height</code> <code>top</code> <code>left</code> <code>right</code> <code>bottom</code>,那么就取 <code>width</code> <code>height</code> <code>top</code> <code>left</code> 属性。<br>优先级而言:宽高优先级最高,<code>top</code> <code>left</code> 其次,<code>right</code> <code>bottom</code> 最后。</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>暂时就写这么多吧,把常见的没搞懂的 CSS 给过了一遍,要想更好的掌握 CSS,必须多多动手实践才可以。<br>如有其他常用的 CSS 属性将会继续更新~</p>]]></content>
<summary type="html">
<blockquote>
<p>对于 CSS,由于过度依赖美工导致 CSS 很弱,所以这次好好学习 CSS相关东西。</p>
</blockquote>
<h1 id="盒子模型"><a href="#盒子模型" class="headerlink" title="盒子模型"><
</summary>
</entry>
<entry>
<title>被人忽视的 DOM API</title>
<link href="https://violetjack.github.io/2018/03/25/%E8%A2%AB%E4%BA%BA%E5%BF%BD%E8%A7%86%E7%9A%84%20DOM%20API/"/>
<id>https://violetjack.github.io/2018/03/25/被人忽视的 DOM API/</id>
<published>2018-03-24T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.257Z</updated>
<content type="html"><![CDATA[<blockquote><p>在框架盛行的年代,还有多少人记得在没有框架时我们如何控制 dom 的行为呢?作者本人也一直忽视了这方面的学习,直到面试问到这个问题,下决心好好认识认识这个 dom api。</p></blockquote><h1 id="node、document-和-element"><a href="#node、document-和-element" class="headerlink" title="node、document 和 element"></a>node、document 和 element</h1><p>在学习 dom api 时对这三者还是挺混乱的。理一下他们之间的关系。</p><h2 id="node"><a href="#node" class="headerlink" title="node"></a>node</h2><p>node 是一个接口,像 document 和 element 都是继承这个接口的。这个接口提供了 dom 节点的获取和操作方法。<br>node 有许多类型,下图列出了一些 node 的类型码。由图可见 element 的类型码为 1,文本节点类型码为 3,注释节点类型码为 8,document 的类型码为 9。</p><p><img src="https://upload-images.jianshu.io/upload_images/1987062-2ff52ed17c0e5c1c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="node type"></p><p>这让我想到了 vue.js 中出现多次的代码:<code>if (child.nodeType === 3) { ... }</code> 其实就是判断当前节点是否为文本节点。</p><h2 id="element"><a href="#element" class="headerlink" title="element"></a>element</h2><p>从上面的内容可知,element 就是一个特殊的 node(nodeType == 1),其实 element 就是 HTML 各类标签,如 </p><p><div> 这类有特殊含义的,能够携带一些特殊属性的节点。所以说 element 可以用 node 的所有 api。</div></p><h2 id="document"><a href="#document" class="headerlink" title="document"></a>document</h2><p>同理,document 也是一个特殊的 node,它与 element 的不同之处在于 document 通常是 DOM 节点,即包含有 head 和 body 元素的一个 node。</p><p>参考:<a href="https://stackoverflow.com/questions/9979172/difference-between-node-object-and-element-object" target="_blank" rel="noopener">Difference between Node object and Element object? - Stack Overflow</a></p><h1 id="api-学习"><a href="#api-学习" class="headerlink" title="api 学习"></a>api 学习</h1><p>首先我发现一点:<strong>所有 dom 操作的起点都是使用 document 去获取各种类型的 node (集合)然后再去执行各类 dom 操作的行为!</strong><br>由于 document 操作的 api 真的很多,所以我选取我想了解的部分学习了。在这里我学习 DOM API 的目的是:</p><blockquote><p>学习 <code>document</code> 对象操作 dom 的方式,拥有脱离框架(jquery、vue等)来操作 dom 的能力。</p></blockquote><h2 id="获取节点"><a href="#获取节点" class="headerlink" title="获取节点"></a>获取节点</h2><ul><li><strong>Document.documentElement</strong> 返回 document 的直属后代元素。</li><li><strong>Document.activeElement</strong> 返回当前正在操作的元素</li><li><strong>Document.body</strong> 返回当前文档的 <code><body></code> 元素。与此类似的还有 Document.head 和 Document.scripts 两个属性返回当前文档的 <code><head></code> 和 <code><script></code> 元素。</li><li><strong>Document.getElementByClassName()</strong> 返回有给定样式名的元素列表</li><li><strong>Document.getElementByTagName()</strong> 返回有给定标签名的元素列表</li><li><strong>document.getElementById()</strong> 返回一个对识别元素的对象引用</li><li><strong>document.querySelector()</strong> 返回文档中第一个匹配指定选择器的元素</li><li><strong>document.querySelectorAll()</strong> 返回一个匹配指定选择器的元素节点列表</li><li><strong>Node.childNodes</strong> 返回一个包含了该节点所有子节点的实时的 <code>NodeList</code> 是“实时的”意思是,如果该节点的子节点发生了变化,<code>NodeList</code> 对象就会自动更新。</li><li><strong>Node.firstChild & Node.lastChild</strong> 返回该节点的第一个子节点或最后一个子节点,如果该节点没有子节点则返回 null。</li><li><strong>Node.previousSibling & Node.nextSibling</strong> 返回与该节点同级的上一个或下一个节点,如果没有返回null。</li><li><strong>Node.ownerDocument</strong> 返回这个元素属于的 <code>Document</code> 对象 。</li><li><strong>Node.parentNode</strong> 返回一个当前结点 <code>Node</code> 的父节点 。</li><li><strong>Node.parentElement</strong> 返回一个当前节点的父节点 <code>Element</code>。</li></ul><h2 id="操作节点"><a href="#操作节点" class="headerlink" title="操作节点"></a>操作节点</h2><ul><li><strong>Document.createComment()</strong> 创建一个新的注释节点并返回它</li><li><strong>Document.createDocumentFragment()</strong> 创建一个新的文档片段</li><li><strong>Document.createElement()</strong> 用给定的标签名创建一个新的元素。</li><li><strong>Document.createTextNode()</strong> 创建一个文字节点</li><li><strong>Document.write()</strong> 向文档中写入内容(与之有类似功能的是 Document.writeln() 不同之处在于后面多了个换行符。)</li><li><strong>Element.innerHTML</strong> 设置或返回元素的内容</li><li><strong>Node.textContent</strong> 获取或设置一个标签内所有子结点及其后代的文本内容。</li><li><strong>Node.appendChild()</strong> 向元素添加新的子节点,作为最后一个子节点。</li><li><strong>Node.cloneNode()</strong> 克隆元素(方法中传参为deep,如果deep为true则深拷贝。)</li><li><strong>Node.insertBefore()</strong> 在指定已有节点前插入新节点(没有 <code>insertAfter</code> 方法。可以使用 <code>insertBefore</code> 方法和 <code>nextSibling</code> 来模拟它。)</li><li><strong>Node.normalize()</strong> 合并元素中相邻文本节点</li><li><strong>Node.removeChild()</strong> 从元素中移除子节点</li><li><strong>Node.replaceChild()</strong> 替换元素中的子节点</li></ul><p>其中 Document 的 <code>createXXX</code> 方法还有一些其他不常用的,如需使用请查阅 MDN。</p><h2 id="其他常用属性和方法"><a href="#其他常用属性和方法" class="headerlink" title="其他常用属性和方法"></a>其他常用属性和方法</h2><ul><li><strong>Element.classList</strong> 返回元素的 class 集合。</li><li><strong>EventTaget.addEventListener()</strong> 注册监听事件</li><li><strong>Node.nodeType</strong> 返回该节点的类型码</li><li><strong>Node.nodeValue</strong> 返回或设置当前节点的值。</li><li><strong>Node.compareDocumentPosition()</strong> 比较当前节点与任意文档中的另一个节点的位置关系。</li><li><strong>Node.contains()</strong> 传入的节点是否为该节点的后代节点。</li><li><strong>Node.hasChildNodes()</strong> 是否拥有子节点</li><li><strong>Node.isEqualNode()</strong> 检查两个元素是否相等</li><li><strong>Node.isSameNode()</strong> 检查两个元素是否为相同的节点</li></ul><p>以上内容均参考了 MDN 上的内容:</p><ul><li>Node <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Node" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/Node</a></li><li>Element <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Element" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/Element</a></li><li>Document <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Document" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/Document</a></li></ul><h1 id="写个操作-DOM-的例子"><a href="#写个操作-DOM-的例子" class="headerlink" title="写个操作 DOM 的例子"></a>写个操作 DOM 的例子</h1><p>接下来就使用这些 API 来进行一些 DOM操作。</p><h2 id="获取各个位置的节点。"><a href="#获取各个位置的节点。" class="headerlink" title="获取各个位置的节点。"></a>获取各个位置的节点。</h2><p>这里写了个小demo:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>get dom<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ul</span> <span class="attr">id</span>=<span class="string">"list"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span><span class="tag"><<span class="name">span</span>></span>hello world 1<span class="tag"></<span class="name">span</span>></span><span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span><span class="tag"><<span class="name">span</span>></span>hello world 2<span class="tag"></<span class="name">span</span>></span><span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span><span class="tag"><<span class="name">span</span>></span>hello world 3<span class="tag"></<span class="name">span</span>></span><span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span><span class="tag"><<span class="name">span</span>></span>hello world 4<span class="tag"></<span class="name">span</span>></span><span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span><span class="tag"><<span class="name">span</span>></span>hello world 5<span class="tag"></<span class="name">span</span>></span><span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">br</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span>></span>commit<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> container = <span class="built_in">document</span>.getElementById(<span class="string">'container'</span>)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'列出所有node'</span>, container.childNodes)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> h1 = <span class="built_in">document</span>.getElementsByTagName(<span class="string">'h1'</span>)[<span class="number">0</span>]</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'获取h1后的元素'</span>, h1.nextSibling)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> uldiv = container.firstChild</span></span><br><span class="line"><span class="javascript"> <span class="keyword">while</span> (uldiv && uldiv.nodeType != <span class="number">1</span>) {</span></span><br><span class="line"><span class="undefined"> uldiv = uldiv.nextSibling</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> ul = uldiv.lastChild</span></span><br><span class="line"><span class="javascript"> <span class="keyword">while</span> (ul && ul.nodeType == <span class="number">3</span>) {</span></span><br><span class="line"><span class="undefined"> ul = ul.previousSibling</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'获取ul中第一个元素内容'</span>, ul.firstChild)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> doc = h1.ownerDocument</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'获取当前 Document 对象'</span>, doc)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> li1 = ul.firstChild</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'获取li的父级节点'</span>, li1.parentElement)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> button = <span class="built_in">document</span>.getElementsByTagName(<span class="string">"button"</span>)[<span class="number">0</span>]</span></span><br><span class="line"><span class="undefined"> button.onclick = log</span></span><br><span class="line"><span class="undefined"> button.focus()</span></span><br><span class="line"><span class="undefined"> button.click()</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'获取正在操作的元素'</span>, <span class="built_in">document</span>.activeElement)</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">log</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'button is clicked'</span>)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure></p><p>最后返回结果如图:<br><img src="https://upload-images.jianshu.io/upload_images/1987062-9f315855a37c0b17.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="返回结果"><br>由于在 chrome 中空格、换行算是文本节点。所以获取最后元素的时候总是会获取到那些文本节点上去。这个要注意的。所以我在代码中使用 <code>nodeType == 1</code> 来区分是否为元素。<br>在上面例子中查找了各种关系的元素,解决日常元素获取问题应该不难了。</p><h2 id="实践创建node、插入node和删除node。"><a href="#实践创建node、插入node和删除node。" class="headerlink" title="实践创建node、插入node和删除node。"></a>实践创建node、插入node和删除node。</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span> <span class="attr">id</span>=<span class="string">"child"</span>></span>Hello Child<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">br</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"buttonGroup"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 父节点向子节点插入元素</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">appendChild</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> container = <span class="built_in">document</span>.getElementById(<span class="string">"container"</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> text = <span class="built_in">document</span>.createElement(<span class="string">"h2"</span>)</span></span><br><span class="line"><span class="javascript"> text.textContent = <span class="string">'Hello New Child'</span></span></span><br><span class="line"><span class="undefined"> container.appendChild(text)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 子节点获取父节点,在父节点后插入元素</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">appendParent</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> child = <span class="built_in">document</span>.getElementById(<span class="string">'child'</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> parent = child.parentElement</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> text = <span class="built_in">document</span>.createElement(<span class="string">"h1"</span>)</span></span><br><span class="line"><span class="javascript"> text.textContent = <span class="string">'Hello Parent'</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> root = parent.parentElement</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined"> root.insertBefore(text, parent.nextSibling)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 在当前元素前插入元素</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">appendPre</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> child = <span class="built_in">document</span>.getElementById(<span class="string">'child'</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> text = <span class="built_in">document</span>.createElement(<span class="string">"h2"</span>)</span></span><br><span class="line"><span class="javascript"> text.textContent = <span class="string">'Hello Pre Child'</span></span></span><br><span class="line"><span class="undefined"> child.parentElement.insertBefore(text, child)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 在当前元素后插入元素</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">appendNext</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> child = <span class="built_in">document</span>.getElementById(<span class="string">'child'</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> text = <span class="built_in">document</span>.createElement(<span class="string">"h2"</span>)</span></span><br><span class="line"><span class="javascript"> text.textContent = <span class="string">'Hello Next Child'</span></span></span><br><span class="line"><span class="undefined"> child.parentElement.insertBefore(text, child.nextSibling)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 移除父元素中最后一个子元素</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">removeEle</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> container = <span class="built_in">document</span>.getElementById(<span class="string">'container'</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (container.lastChild) {</span></span><br><span class="line"><span class="undefined"> container.removeChild(container.lastChild)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 替换父元素中的子元素</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">replaceEle</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> child = <span class="built_in">document</span>.getElementById(<span class="string">"child"</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> newNode = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>)</span></span><br><span class="line"><span class="xml"> newNode.innerHTML = "<span class="tag"><<span class="name">button</span>></span>button<span class="tag"></<span class="name">button</span>></span>hello new node replaced"</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> parent = child.parentElement</span></span><br><span class="line"><span class="undefined"> parent.replaceChild(newNode, child)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 创建按钮组</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> ButtonGroup = <span class="built_in">document</span>.getElementById(<span class="string">"buttonGroup"</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> EventList = [ </span></span><br><span class="line"><span class="javascript"> <span class="string">"appendChild"</span>, </span></span><br><span class="line"><span class="javascript"> <span class="string">"appendParent"</span>, </span></span><br><span class="line"><span class="javascript"> <span class="string">"appendPre"</span>, </span></span><br><span class="line"><span class="javascript"> <span class="string">"appendNext"</span>, </span></span><br><span class="line"><span class="javascript"> <span class="string">"removeEle"</span>, </span></span><br><span class="line"><span class="javascript"> <span class="string">"replaceEle"</span> </span></span><br><span class="line"><span class="undefined"> ]</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> ButtonArr = []</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">of</span> EventList) {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> btn = <span class="built_in">document</span>.createElement(<span class="string">'button'</span>)</span></span><br><span class="line"><span class="undefined"> btn.textContent = key</span></span><br><span class="line"><span class="javascript"> btn.onclick = <span class="built_in">eval</span>(key)</span></span><br><span class="line"><span class="undefined"> ButtonArr.push(btn)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span> (<span class="keyword">var</span> b <span class="keyword">of</span> ButtonArr) {</span></span><br><span class="line"><span class="undefined"> ButtonGroup.appendChild(b)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>以上代码实现了<strong>在各个位置插入元素</strong>和<strong>元素的删除替换</strong>,点击<a href="https://jsfiddle.net/VioletJack/g4nt26yf/1/" target="_blank" rel="noopener">此处</a>查看运行结果。</p><h2 id="简单实现-v-for、v-text、v-html、v-on-和-v-model-这些功能。"><a href="#简单实现-v-for、v-text、v-html、v-on-和-v-model-这些功能。" class="headerlink" title="简单实现 v-for、v-text、v-html、v-on 和 v-model 这些功能。"></a>简单实现 v-for、v-text、v-html、v-on 和 v-model 这些功能。</h2><p>好吧,作为一个 Vue.js 爱好者,绕不开的想到了 Vue.js 操作 DOM 的一些功能。这里就试着简单实现下(不涉及 Virtual DOM,只是单纯的 DOM 修改)。<br>如果对 Vue 命令不了解可以去官网看看这些<a href="https://cn.vuejs.org/v2/api/#%E6%8C%87%E4%BB%A4" target="_blank" rel="noopener">指令</a>的用法。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Hello Ele<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>v-text<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span>></span>{{ message }}<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>v-html<span class="tag"></<span class="name">h1</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">v-html</span>=<span class="string">"messagespan"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>v-model<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">v-model</span>=<span class="string">"message"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>v-on<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">id</span>=<span class="string">"myInput"</span> <span class="attr">v-on:blur</span>=<span class="string">"blur"</span> <span class="attr">v-on:focus</span>=<span class="string">"focus"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>v-for<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ul</span>></span><span class="tag"></<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="comment">// v-text</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> message = <span class="string">"Hello World"</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> messagespan = <span class="string">"<span>Hello World</span>"</span></span></span><br><span class="line"><span class="undefined"> </span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> spans = <span class="built_in">document</span>.getElementsByTagName(<span class="string">"span"</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span> (<span class="keyword">var</span> span <span class="keyword">of</span> spans) {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (span.textContent == <span class="string">"{{ message }}"</span>) {</span></span><br><span class="line"><span class="undefined"> span.textContent = message</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// v-html</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> container = <span class="built_in">document</span>.getElementById(<span class="string">"container"</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> divs = container.getElementsByTagName(<span class="string">"div"</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span>(<span class="keyword">var</span> div <span class="keyword">of</span> divs){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (div.getAttribute(<span class="string">"v-html"</span>) == <span class="string">"messagespan"</span>) {</span></span><br><span class="line"><span class="undefined"> div.innerHTML = messagespan</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// v-model</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> inputs = container.getElementsByTagName(<span class="string">"input"</span>)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span> (<span class="keyword">var</span> input <span class="keyword">of</span> inputs) {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (input.getAttribute(<span class="string">"v-model"</span>) == <span class="string">"message"</span>) {</span></span><br><span class="line"><span class="javascript"> input.setAttribute(<span class="string">"value"</span>, message)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// v-on</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> myInput = <span class="built_in">document</span>.getElementById(<span class="string">"myInput"</span>)</span></span><br><span class="line"><span class="javascript"> myInput.onfocus = <span class="built_in">eval</span>(myInput.getAttribute(<span class="string">"v-on:focus"</span>))</span></span><br><span class="line"><span class="javascript"> myInput.onblur = <span class="built_in">eval</span>(myInput.getAttribute(<span class="string">"v-on:blur"</span>))</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">focus</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> myInput.setAttribute(<span class="string">"value"</span>, <span class="string">"focus"</span>)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">blur</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> myInput.setAttribute(<span class="string">"value"</span>, <span class="string">"blur"</span>)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// v-for</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> liContents = [</span></span><br><span class="line"><span class="javascript"> <span class="string">"jack"</span>,</span></span><br><span class="line"><span class="javascript"> <span class="string">"rose"</span>,</span></span><br><span class="line"><span class="javascript"> <span class="string">"james"</span>,</span></span><br><span class="line"><span class="javascript"> <span class="string">"wade"</span>,</span></span><br><span class="line"><span class="javascript"> <span class="string">"jordan"</span></span></span><br><span class="line"><span class="undefined"> ]</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> liElementList = []</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span>(<span class="keyword">var</span> content <span class="keyword">of</span> liContents) {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> li = <span class="built_in">document</span>.createElement(<span class="string">"li"</span>)</span></span><br><span class="line"><span class="xml"> li.innerHTML = `<span class="tag"><<span class="name">label</span>></span><span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"checkbox"</span>/></span><span class="tag"><<span class="name">span</span>></span>${content}<span class="tag"></<span class="name">span</span>></span><span class="tag"></<span class="name">label</span>></span>`</span></span><br><span class="line"><span class="undefined"> liElementList.push(li)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> ul = container.getElementsByTagName(<span class="string">"ul"</span>)[<span class="number">0</span>]</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span> (<span class="keyword">var</span> liEle <span class="keyword">of</span> liElementList) {</span></span><br><span class="line"><span class="undefined"> ul.appendChild(liEle)</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></p><p>点击<a href="https://jsfiddle.net/VioletJack/cpsrzbx5/5/" target="_blank" rel="noopener">此处</a>看效果。最后结果如图:<br><img src="https://upload-images.jianshu.io/upload_images/1987062-8c87d7451a8f3abf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="显示结果"></p><p>简单实现了 Vue.js 指令的这些功能,其实在 Vue.js 源码中也是用了这些 dom 操作的 api 来做的。<br>更多 Vue 源码中的 DOM 操作可以看下我的 <a href="https://www.jianshu.com/p/9db8eb16d76f" target="_blank" rel="noopener">《Vue.js 源码学习六 —— VNode虚拟DOM学习》</a>这篇文章中。</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>无论什么框架,其实都是万变不离其宗。最终都是用最基础的 API 来实现的各种功能。所以学好基础知识是非常重要的~<br>PS:还是 MDN 靠谱,w3school 的资料虽然也挺多,但是感觉不是很靠谱……以后查资料尽量去 MDN 英文网站去查(中文网站翻译有些问题)。</p>]]></content>
<summary type="html">
<blockquote>
<p>在框架盛行的年代,还有多少人记得在没有框架时我们如何控制 dom 的行为呢?作者本人也一直忽视了这方面的学习,直到面试问到这个问题,下决心好好认识认识这个 dom api。</p>
</blockquote>
<h1 id="node、docume
</summary>
</entry>
<entry>
<title>前端优化学习</title>
<link href="https://violetjack.github.io/2018/03/23/%E5%89%8D%E7%AB%AF%E4%BC%98%E5%8C%96%E5%AD%A6%E4%B9%A0/"/>
<id>https://violetjack.github.io/2018/03/23/前端优化学习/</id>
<published>2018-03-22T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.255Z</updated>
<content type="html"><![CDATA[<blockquote><p>简单花了一些时间看了几篇前端优化的文章,文章作者写的都很详细。从中学到了不少东西。在这里就不班门弄斧的多讲了。只是在几篇文章的基础上一个小小的总结。</p></blockquote><h1 id="一些性能优化资料"><a href="#一些性能优化资料" class="headerlink" title="一些性能优化资料"></a>一些性能优化资料</h1><ul><li><a href="https://developer.yahoo.com/performance/rules.html" target="_blank" rel="noopener">Best Practices for Speeding Up Your Web Site</a></li><li><a href="https://github.com/wy-ei/notebook/issues/34" target="_blank" rel="noopener">前端性能优化相关</a></li><li><a href="https://github.com/Findow-team/Blog/issues/11" target="_blank" rel="noopener">2017前端性能优化清单</a></li><li><a href="https://segmentfault.com/a/1190000003646305" target="_blank" rel="noopener">前端性能优化指南</a></li><li>动物书:<a href="https://book.douban.com/subject/5362856/" target="_blank" rel="noopener">《高性能JavaScript》</a>、<a href="https://book.douban.com/subject/4719162/" target="_blank" rel="noopener">《高性能网站建设进阶指南》</a>、<a href="https://book.douban.com/subject/3132277/" target="_blank" rel="noopener">《高性能网站建设指南》</a></li></ul><h1 id="优化方法总结"><a href="#优化方法总结" class="headerlink" title="优化方法总结"></a>优化方法总结</h1><p>参照着这些资料,总结一下一些优化的知识点。</p><ul><li>减少 DOM 和 JavaScript 的交互。避免重复读写,而要先遍历读再遍历写。</li><li>使用 requireAnimationFrame 方法避免丢帧。</li><li>使用 debounce 和 throttle 进行节流和消抖,用于向滚动、重复触发的这类场景</li><li>CSS放头部,JavaScript 放底部。CSS需要在DOM显示前加载完,否则界面会没有样式一段时间。JavaScript 中可能有操作 DOM 行为,应该在 DOM 加载完后执行。</li><li>JavaScript 不慢,DOM 很慢。所以避免 DOM 大量渲染。<ul><li>PS:我之前做一个表单,三十几页内容,为了方便就将所有页面加载到 DOM 中,然后显示当前页隐藏其他页,卡的飞起。期初还以为是 JavaScript 逻辑计算卡住了呢。</li></ul></li><li>代码分包,优先加载核心业务。</li><li>JS 脚本合并</li><li>GPU硬件加速用于图层。</li><li>图片优化压缩,图片 lazyload 延时获取</li><li>利用 defer、async 属性异步加载脚本</li><li>合理使用缓存,将一些静态的东西使用缓存存本地,减少 HTTP 交互。</li><li>优化 HTTP 接口减少 HTTP 传输次数</li><li>优化结果的一些方面<ul><li>首屏加载</li><li>帧数每秒60帧</li><li>反应时间100毫秒</li></ul></li><li>理解DOM绘制和布局</li><li>使用 JSON 交换数据</li><li>高效使用 HTML 标签和 CSS 样式,好的写法能够减少性能的消耗。</li><li>CSS 和 JavaScript 的压缩合并。</li><li>图片优化:压缩图片、Base64图片、Sprite雪碧图</li><li>服务器渲染 SSR,在服务器端将页面加载完成传给客户端,可以减少首次加载的逻辑处理,加快首屏加载时间。<ul><li>这是由一定代价的,需要消耗更多的服务器资源,如果是大项目,可能需要加很多服务器来满足SSR。</li></ul></li><li>减少 cookie 大小,因为 cookie 会在每一次 HTTP 请求时被传输, cookie 的大小影响传输速度。</li></ul><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>其实,个人感觉:<strong>前端的优化理论上知道是一回事儿。真正要把优化玩好,还是需要在实际工作中不断去试错、实践、和解决问题,才能更快更好的去完成一个好的产品。</strong><br>本文记录下前端优化的一些知识点,便于之后工作中使用吧~</p>]]></content>
<summary type="html">
<blockquote>
<p>简单花了一些时间看了几篇前端优化的文章,文章作者写的都很详细。从中学到了不少东西。在这里就不班门弄斧的多讲了。只是在几篇文章的基础上一个小小的总结。</p>
</blockquote>
<h1 id="一些性能优化资料"><a href="#一些性
</summary>
</entry>
<entry>
<title>element 源码学习三 —— select 组件源码学习</title>
<link href="https://violetjack.github.io/2018/03/20/element-code-03/"/>
<id>https://violetjack.github.io/2018/03/20/element-code-03/</id>
<published>2018-03-19T16:00:00.000Z</published>
<updated>2018-03-21T05:32:55.813Z</updated>
<content type="html"><![CDATA[<blockquote><p>select 选择器是个比较复杂的组件了,通过不同的配置可以有多种用法。有必要单独学习学习。</p></blockquote><h1 id="整体结构"><a href="#整体结构" class="headerlink" title="整体结构"></a>整体结构</h1><p>以下是 select 的 template 结构,已去掉了一部分代码便于查看整体结构:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">template</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span></span><br><span class="line"> <span class="comment"><!-- 多选 --></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-if</span>=<span class="string">"multiple"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">ref</span>=<span class="string">"tags"</span>></span></span><br><span class="line"> <span class="comment"><!-- collapse tags 多选时是否将选中值按文字的形式展示 --></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">v-if</span>=<span class="string">"collapseTags && selected.length"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">el-tag</span></span></span><br><span class="line"><span class="tag"> <span class="attr">type</span>=<span class="string">"info"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">disable-transitions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"el-select__tags-text"</span>></span>{{ selected[0].currentLabel }}<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-tag</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">el-tag</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-if</span>=<span class="string">"selected.length > 1"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">type</span>=<span class="string">"info"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">disable-transitions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"el-select__tags-text"</span>></span>+ {{ selected.length - 1 }}<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-tag</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="comment"><!-- 多选,多个 el-tag 组成 --></span></span><br><span class="line"> <span class="tag"><<span class="name">transition-group</span> @<span class="attr">after-leave</span>=<span class="string">"resetInputHeight"</span> <span class="attr">v-if</span>=<span class="string">"!collapseTags"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">el-tag</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-for</span>=<span class="string">"item in selected"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:key</span>=<span class="string">"getValueKey(item)"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">type</span>=<span class="string">"info"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">disable-transitions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"el-select__tags-text"</span>></span>{{ item.currentLabel }}<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-tag</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">transition-group</span>></span></span><br><span class="line"> <span class="comment"><!-- 可输入文本的查询框 --></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-model</span>=<span class="string">"query"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-if</span>=<span class="string">"filterable"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">ref</span>=<span class="string">"input"</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="comment"><!-- 显示结果框 read-only --></span></span><br><span class="line"> <span class="tag"><<span class="name">el-input</span></span></span><br><span class="line"><span class="tag"> <span class="attr">ref</span>=<span class="string">"reference"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-model</span>=<span class="string">"selectedLabel"</span>></span></span><br><span class="line"> <span class="comment"><!-- 用户显示清空和向下箭头 --></span></span><br><span class="line"> <span class="tag"><<span class="name">i</span> <span class="attr">slot</span>=<span class="string">"suffix"</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-input</span>></span></span><br><span class="line"> <span class="comment"><!-- 下拉菜单 --></span></span><br><span class="line"> <span class="tag"><<span class="name">transition</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">el-select-menu</span></span></span><br><span class="line"><span class="tag"> <span class="attr">ref</span>=<span class="string">"popper"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-show</span>=<span class="string">"visible && emptyText !== false"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">el-scrollbar</span></span></span><br><span class="line"><span class="tag"> <span class="attr">tag</span>=<span class="string">"ul"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">wrap-class</span>=<span class="string">"el-select-dropdown__wrap"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">view-class</span>=<span class="string">"el-select-dropdown__list"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">ref</span>=<span class="string">"scrollbar"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-show</span>=<span class="string">"options.length > 0 && !loading"</span>></span></span><br><span class="line"> <span class="comment"><!-- 默认项(创建条目) --></span></span><br><span class="line"> <span class="tag"><<span class="name">el-option</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:value</span>=<span class="string">"query"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">created</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-if</span>=<span class="string">"showNewOption"</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-option</span>></span></span><br><span class="line"> <span class="comment"><!-- 插槽,用于放 option 和 option-group --></span></span><br><span class="line"> <span class="tag"><<span class="name">slot</span>></span><span class="tag"></<span class="name">slot</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-scrollbar</span>></span></span><br><span class="line"> <span class="comment"><!-- loading 加载中文本 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-if</span>=<span class="string">"emptyText &&</span></span></span><br><span class="line"><span class="tag"><span class="string"> (!allowCreate || loading || (allowCreate && options.length === 0 ))"</span>></span></span><br><span class="line"> {{ emptyText }}</span><br><span class="line"> <span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">el-select-menu</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">transition</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">template</span>></span></span><br></pre></td></tr></table></figure></p><p>具体都写在注释中了~从上面内容中可以看到,select 考虑了很多情况,如单选、多选、搜索、下拉框、图标等等。并且使用 slot 插槽来获取开发者传递的 option 和 option-group 组件。<br>可以发现在 select 中使用了多个外部组件,也就是说 el-select 是由多个组件组装成的一个复杂组件~</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// components</span></span><br><span class="line"><span class="keyword">import</span> ElInput <span class="keyword">from</span> <span class="string">'element-ui/packages/input'</span>;</span><br><span class="line"><span class="keyword">import</span> ElSelectMenu <span class="keyword">from</span> <span class="string">'./select-dropdown.vue'</span>;</span><br><span class="line"><span class="keyword">import</span> ElOption <span class="keyword">from</span> <span class="string">'./option.vue'</span>;</span><br><span class="line"><span class="keyword">import</span> ElTag <span class="keyword">from</span> <span class="string">'element-ui/packages/tag'</span>;</span><br><span class="line"><span class="keyword">import</span> ElScrollbar <span class="keyword">from</span> <span class="string">'element-ui/packages/scrollbar'</span>;</span><br></pre></td></tr></table></figure><h1 id="select-要实现的功能"><a href="#select-要实现的功能" class="headerlink" title="select 要实现的功能"></a>select 要实现的功能</h1><p>参照<a href="http://element-cn.eleme.io/#/zh-CN/component/select" target="_blank" rel="noopener">官方文档</a>的内容罗列出 select 的一些功能,后面跟上我对功能实现的理解:</p><ul><li>单选 —— 点击 <code>select</code> 弹出下拉框,点击 <code>option</code> 完成赋值。</li><li>禁用 —— <code>select</code> 和 <code>option</code> 都有 <code>disabled</code> 选项用于禁用。</li><li>清空 —— 如果 <code>select</code> 中有内容,鼠标悬浮在 <code>input</code> 上显示删除图标,点击执行删除操作。</li><li>多选(平铺展示和数字显示数量两种方式) —— 参数 model 变为数组,点击下拉菜单中的选项添加或删除数组中的值。</li><li>自定义模板 —— option 中定义了 <code>slot</code> 插槽,默认加了 <code>span</code> 显示内容。可以修改 <code>el-option</code> 标签中内容来自定义模板。</li><li>分组 —— 使用 option-group 组件来实现分组效果。</li><li>搜索 —— 通过正则匹配搜索项,不符合搜索项的控制 v-show 隐藏</li><li>创建条目 —— 在 <code>select</code> 中添加额外 <code>option</code>(一般 <code>option</code> 都是通过 <code>slot</code> 插槽传递的),如允许创建条目,则显示这条 <code>option</code> ,<code>option</code> 的内容显示为查询内容。</li></ul><h1 id="从几个问题去看源码逻辑"><a href="#从几个问题去看源码逻辑" class="headerlink" title="从几个问题去看源码逻辑"></a>从几个问题去看源码逻辑</h1><h2 id="如何实现基本单选功能?"><a href="#如何实现基本单选功能?" class="headerlink" title="如何实现基本单选功能?"></a>如何实现基本单选功能?</h2><p>分析下基本功能:点击 input,显示下拉菜单;鼠标选中一项 option,隐藏下拉菜单;input 中显示选中的结果。<br>所以这里看下显示内容的 input 都有些什么事件:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">@focus="handleFocus" // 处理 焦点</span><br><span class="line">@blur="handleBlur" // 处理 焦点 离开</span><br><span class="line">@keyup.native="debouncedOnInputChange"</span><br><span class="line">@keydown.native.down.stop.prevent="navigateOptions('next')" // 向下按键,移动到下一个 option</span><br><span class="line">@keydown.native.up.stop.prevent="navigateOptions('prev')" // 向上按键,移动到上一个 option</span><br><span class="line">@keydown.native.enter.prevent="selectOption" // 回车按键,选中option</span><br><span class="line">@keydown.native.esc.stop.prevent="visible = false" // esc按键,隐藏下拉框</span><br><span class="line">@keydown.native.tab="visible = false" // tab按键,跳转到下一个文本框,隐藏下拉框</span><br><span class="line">@paste.native="debouncedOnInputChange" // </span><br><span class="line">@mouseenter.native="inputHovering = true" // mouse enter 事件</span><br><span class="line">@mouseleave.native="inputHovering = false" // mouse leave 事件</span><br></pre></td></tr></table></figure></p><p>从上面的这些事件中可以知道:选中方法为 <code>selectOption</code>(从英文字面意思都能知道~);显示下拉框通过 <code>visible</code> 属性控制;以及其他按键的一些功能。这里主要主要看看 <code>selectOption</code> 方法。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">selectOption() {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.visible) {</span><br><span class="line"> <span class="keyword">this</span>.toggleMenu();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.options[<span class="keyword">this</span>.hoverIndex]) {</span><br><span class="line"> <span class="keyword">this</span>.handleOptionSelect(<span class="keyword">this</span>.options[<span class="keyword">this</span>.hoverIndex]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>逻辑就是,如果下拉框未显示则执行 <code>toggleMenu</code> 方法触发下拉框,如果已显示下拉框则处理选择 option 的过程。看看这个 <code>toggleMenu</code> 方法:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">toggleMenu() {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.selectDisabled) {</span><br><span class="line"> <span class="keyword">this</span>.visible = !<span class="keyword">this</span>.visible;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.visible) {</span><br><span class="line"> (<span class="keyword">this</span>.$refs.input || <span class="keyword">this</span>.$refs.reference).focus();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>其实就是控制下拉菜单的显示和隐藏。如果显示的时候定焦在 <code>input</code> 和 <code>reference</code> 上,它们其实就是单选和多选的 input 框(多选 input 定义了 <code>ref="input"</code> 单选 input 定义了 <code>ref="reference"</code>)。<br>至此,下拉菜单的显示与隐藏解决了。然后我们去找 option 点击事件:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 处理选项选中事件</span></span><br><span class="line">handleOptionSelect(option) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.multiple) {</span><br><span class="line"> <span class="comment">// 多选</span></span><br><span class="line"> <span class="keyword">const</span> value = <span class="keyword">this</span>.value.slice();</span><br><span class="line"> <span class="keyword">const</span> optionIndex = <span class="keyword">this</span>.getValueIndex(value, option.value);</span><br><span class="line"> <span class="keyword">if</span> (optionIndex > <span class="number">-1</span>) {</span><br><span class="line"> <span class="comment">// 已选中,从数组中移除</span></span><br><span class="line"> value.splice(optionIndex, <span class="number">1</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">this</span>.multipleLimit <= <span class="number">0</span> || value.length < <span class="keyword">this</span>.multipleLimit) {</span><br><span class="line"> <span class="comment">// 未选中,传入数组</span></span><br><span class="line"> value.push(option.value);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.$emit(<span class="string">'input'</span>, value);</span><br><span class="line"> <span class="keyword">this</span>.emitChange(value);</span><br><span class="line"> <span class="keyword">if</span> (option.created) {</span><br><span class="line"> <span class="keyword">this</span>.query = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">this</span>.handleQueryChange(<span class="string">''</span>);</span><br><span class="line"> <span class="keyword">this</span>.inputLength = <span class="number">20</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 查询</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.filterable) <span class="keyword">this</span>.$refs.input.focus();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 单选</span></span><br><span class="line"> <span class="keyword">this</span>.$emit(<span class="string">'input'</span>, option.value);</span><br><span class="line"> <span class="keyword">this</span>.emitChange(option.value);</span><br><span class="line"> <span class="keyword">this</span>.visible = <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 渲染完成后</span></span><br><span class="line"> <span class="keyword">this</span>.$nextTick(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">this</span>.scrollToOption(option);</span><br><span class="line"> <span class="keyword">this</span>.setSoftFocus();</span><br><span class="line"> });</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>处理选中事件考虑了单选和多选两种情况。<br>如果是多选,检索选中 option 是否在 <code>value</code> 数组中,有则移除、无则添加到 <code>value</code> 数组中。然后 <code>$emit</code> 触发 <code>input</code> 事件,执行 <code>emitChange</code> 方法。如果 option 的 <code>created</code> 为 true,则清空查询内容。<br>如果是单选,<code>$emit</code> 触发 <code>input</code> 事件将选中值传递给父组件,执行 <code>emitChange</code> 方法,最后隐藏下拉菜单。<br>最后使用 <code>$nextTick</code> 方法处理下界面。<br>到这里,选中 option 后下拉菜单消失问题解决,只剩下显示结果到 input 中了。这个显示结果的过程是通过对 <code>visible</code> 属性的监听来完成的(一开始以为在 <code>emitChange</code> 结果发现那只是触发改变事件的)。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">visible(val) {</span><br><span class="line"> <span class="comment">// 在下拉菜单隐藏时</span></span><br><span class="line"> <span class="keyword">if</span> (!val) {</span><br><span class="line"> <span class="comment">// 处理图标</span></span><br><span class="line"> <span class="keyword">this</span>.handleIconHide();</span><br><span class="line"> <span class="comment">// 广播下拉菜单销毁事件</span></span><br><span class="line"> <span class="keyword">this</span>.broadcast(<span class="string">'ElSelectDropdown'</span>, <span class="string">'destroyPopper'</span>);</span><br><span class="line"> <span class="comment">// 取消焦点</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.$refs.input) {</span><br><span class="line"> <span class="keyword">this</span>.$refs.input.blur();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 重置过程</span></span><br><span class="line"> <span class="keyword">this</span>.query = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">this</span>.previousQuery = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">this</span>.selectedLabel = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">this</span>.inputLength = <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">this</span>.resetHoverIndex();</span><br><span class="line"> <span class="keyword">this</span>.$nextTick(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.$refs.input &&</span><br><span class="line"> <span class="keyword">this</span>.$refs.input.value === <span class="string">''</span> &&</span><br><span class="line"> <span class="keyword">this</span>.selected.length === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">this</span>.currentPlaceholder = <span class="keyword">this</span>.cachedPlaceHolder;</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="comment">// 如果不是多选,进行赋值现在 input 中</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.multiple) {</span><br><span class="line"> <span class="comment">// selected 为当前选中的 option</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.selected) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.filterable && <span class="keyword">this</span>.allowCreate &&</span><br><span class="line"> <span class="keyword">this</span>.createdSelected && <span class="keyword">this</span>.createdOption) {</span><br><span class="line"> <span class="keyword">this</span>.selectedLabel = <span class="keyword">this</span>.createdLabel;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.selectedLabel = <span class="keyword">this</span>.selected.currentLabel;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 查询结果</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.filterable) <span class="keyword">this</span>.query = <span class="keyword">this</span>.selectedLabel;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 下拉菜单显示</span></span><br><span class="line"> <span class="comment">// 处理图片显示</span></span><br><span class="line"> <span class="keyword">this</span>.handleIconShow();</span><br><span class="line"> <span class="comment">// 广播下拉菜单更新事件</span></span><br><span class="line"> <span class="keyword">this</span>.broadcast(<span class="string">'ElSelectDropdown'</span>, <span class="string">'updatePopper'</span>);</span><br><span class="line"> <span class="comment">// 处理查询事件</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.filterable) {</span><br><span class="line"> <span class="keyword">this</span>.query = <span class="keyword">this</span>.remote ? <span class="string">''</span> : <span class="keyword">this</span>.selectedLabel;</span><br><span class="line"> <span class="keyword">this</span>.handleQueryChange(<span class="keyword">this</span>.query);</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.multiple) {</span><br><span class="line"> <span class="keyword">this</span>.$refs.input.focus();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.remote) {</span><br><span class="line"> <span class="keyword">this</span>.broadcast(<span class="string">'ElOption'</span>, <span class="string">'queryChange'</span>, <span class="string">''</span>);</span><br><span class="line"> <span class="keyword">this</span>.broadcast(<span class="string">'ElOptionGroup'</span>, <span class="string">'queryChange'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.broadcast(<span class="string">'ElInput'</span>, <span class="string">'inputSelect'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 触发 visible-change 事件</span></span><br><span class="line"> <span class="keyword">this</span>.$emit(<span class="string">'visible-change'</span>, val);</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>从 template 中可知,显示结果的 input 绑定的 <code>v-model</code> 是 <code>selectedLabel</code>,而 select 是通过获取下拉菜单的显示与隐藏事件来执行结果显示部分的功能的。最终 <code>selectedLabel</code> 获得到了选中的 option 的 <code>label</code> 内容。<br>这样,从 <strong>点击-单选-显示</strong> 的流程就实现了。还是很简单的。</p><h2 id="如何实现多选,多选选中后-option-右侧的勾以及-input-中的-tag-如何显示?"><a href="#如何实现多选,多选选中后-option-右侧的勾以及-input-中的-tag-如何显示?" class="headerlink" title="如何实现多选,多选选中后 option 右侧的勾以及 input 中的 tag 如何显示?"></a>如何实现多选,多选选中后 option 右侧的勾以及 input 中的 tag 如何显示?</h2><p>关于多选,在刚才讲单选的时候提及了一些了。所以有些代码就不贴出浪费篇幅了。具体逻辑如下:<br>先点击 input 执行 <code>selectOption</code> 方法显示下拉菜单,然后点击下拉菜单中的 option,执行 <code>handleOptionSelect</code> 方法将 option 的值都传给 <code>value</code> 数组。此时 <code>value</code> 数组改变,触发 watch 中的 <code>value</code> 变化监听方法。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">value(val) {</span><br><span class="line"> <span class="comment">// 多选</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.multiple) {</span><br><span class="line"> <span class="keyword">this</span>.resetInputHeight();</span><br><span class="line"> <span class="keyword">if</span> (val.length > <span class="number">0</span> || (<span class="keyword">this</span>.$refs.input && <span class="keyword">this</span>.query !== <span class="string">''</span>)) {</span><br><span class="line"> <span class="keyword">this</span>.currentPlaceholder = <span class="string">''</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.currentPlaceholder = <span class="keyword">this</span>.cachedPlaceHolder;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.filterable && !<span class="keyword">this</span>.reserveKeyword) {</span><br><span class="line"> <span class="keyword">this</span>.query = <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">this</span>.handleQueryChange(<span class="keyword">this</span>.query);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.setSelected();</span><br><span class="line"> <span class="comment">// 非多选查询</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.filterable && !<span class="keyword">this</span>.multiple) {</span><br><span class="line"> <span class="keyword">this</span>.inputLength = <span class="number">20</span>;</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>以上代码关键是执行了 <code>setSelected</code> 方法:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置选择项</span></span><br><span class="line">setSelected() {</span><br><span class="line"> <span class="comment">// 单选</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.multiple) {</span><br><span class="line"> <span class="keyword">let</span> option = <span class="keyword">this</span>.getOption(<span class="keyword">this</span>.value);</span><br><span class="line"> <span class="comment">// created 是指创建出来的 option,这里指 allow-create 创建的 option 项</span></span><br><span class="line"> <span class="keyword">if</span> (option.created) {</span><br><span class="line"> <span class="keyword">this</span>.createdLabel = option.currentLabel;</span><br><span class="line"> <span class="keyword">this</span>.createdSelected = <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.createdSelected = <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.selectedLabel = option.currentLabel;</span><br><span class="line"> <span class="keyword">this</span>.selected = option;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.filterable) <span class="keyword">this</span>.query = <span class="keyword">this</span>.selectedLabel;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 遍历获取 option</span></span><br><span class="line"> <span class="keyword">let</span> result = [];</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(<span class="keyword">this</span>.value)) {</span><br><span class="line"> <span class="keyword">this</span>.value.forEach(<span class="function"><span class="params">value</span> =></span> {</span><br><span class="line"> result.push(<span class="keyword">this</span>.getOption(value));</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 赋值</span></span><br><span class="line"> <span class="keyword">this</span>.selected = result;</span><br><span class="line"> <span class="keyword">this</span>.$nextTick(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="comment">// 重置 input 高度</span></span><br><span class="line"> <span class="keyword">this</span>.resetInputHeight();</span><br><span class="line"> });</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>可以看到如果是多选,那么将 <code>value</code> 数组遍历,获取相应的 <code>option</code> 值,传给 <code>selected</code>。而多选界面其实就是对于这个 <code>selected</code> 的 v-for 遍历显示。显示的标签使用的是 element 的另外一个组件 <a href="http://element-cn.eleme.io/#/zh-CN/component/tag" target="_blank" rel="noopener">el-tag</a><br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">el-tag</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-for</span>=<span class="string">"item in selected"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:key</span>=<span class="string">"getValueKey(item)"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"el-select__tags-text"</span>></span>{{ item.currentLabel }}<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"></<span class="name">el-tag</span>></span></span><br></pre></td></tr></table></figure></p><p>这里顺便提一句: option 的 <code>created</code> 参数用于标识是 <code>select</code> 组件中创建的那个用于创建条目的 <code>option</code>。而从 slot 插槽传入的 option 是不用传 <code>created</code> 参数的。</p><h2 id="如何实现搜索功能?"><a href="#如何实现搜索功能?" class="headerlink" title="如何实现搜索功能?"></a>如何实现搜索功能?</h2><p>从 template 中可知,select 有两个 input,一个用于显示结果,一个则用于查询搜索。我们来看下搜索内容的 input 文本框如何实现搜索功能:<br>在 input 中有 <code>@input="e => handleQueryChange(e.target.value)"</code>这么一段代码。所以,handleQueryChange 方法就是关键所在了。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 处理查询改变</span></span><br><span class="line">handleQueryChange(val) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.previousQuery === val) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">if</span> (</span><br><span class="line"> <span class="keyword">this</span>.previousQuery === <span class="literal">null</span> &&</span><br><span class="line"> (<span class="keyword">typeof</span> <span class="keyword">this</span>.filterMethod === <span class="string">'function'</span> || <span class="keyword">typeof</span> <span class="keyword">this</span>.remoteMethod === <span class="string">'function'</span>)</span><br><span class="line"> ) {</span><br><span class="line"> <span class="keyword">this</span>.previousQuery = val;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.previousQuery = val;</span><br><span class="line"> <span class="keyword">this</span>.$nextTick(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.visible) <span class="keyword">this</span>.broadcast(<span class="string">'ElSelectDropdown'</span>, <span class="string">'updatePopper'</span>);</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">this</span>.hoverIndex = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.multiple && <span class="keyword">this</span>.filterable) {</span><br><span class="line"> <span class="keyword">const</span> length = <span class="keyword">this</span>.$refs.input.value.length * <span class="number">15</span> + <span class="number">20</span>;</span><br><span class="line"> <span class="keyword">this</span>.inputLength = <span class="keyword">this</span>.collapseTags ? <span class="built_in">Math</span>.min(<span class="number">50</span>, length) : length;</span><br><span class="line"> <span class="keyword">this</span>.managePlaceholder();</span><br><span class="line"> <span class="keyword">this</span>.resetInputHeight();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.remote && <span class="keyword">typeof</span> <span class="keyword">this</span>.remoteMethod === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">this</span>.hoverIndex = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">this</span>.remoteMethod(val);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="keyword">this</span>.filterMethod === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">this</span>.filterMethod(val);</span><br><span class="line"> <span class="keyword">this</span>.broadcast(<span class="string">'ElOptionGroup'</span>, <span class="string">'queryChange'</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.filteredOptionsCount = <span class="keyword">this</span>.optionsCount;</span><br><span class="line"> <span class="keyword">this</span>.broadcast(<span class="string">'ElOption'</span>, <span class="string">'queryChange'</span>, val);</span><br><span class="line"> <span class="keyword">this</span>.broadcast(<span class="string">'ElOptionGroup'</span>, <span class="string">'queryChange'</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.defaultFirstOption && (<span class="keyword">this</span>.filterable || <span class="keyword">this</span>.remote) && <span class="keyword">this</span>.filteredOptionsCount) {</span><br><span class="line"> <span class="keyword">this</span>.checkDefaultFirstOption();</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>其中,<code>remoteMethod</code> 和 <code>filterMethod</code> 方法是自定义的远程查询和本地过滤方法。如果没有自定义的这两个方法,则会触发广播给 <code>option</code> 和 <code>option-group</code> 组件 <code>queryChange</code> 方法。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// option.vue</span></span><br><span class="line">queryChange(query) {</span><br><span class="line"> <span class="keyword">let</span> parsedQuery = <span class="built_in">String</span>(query).replace(<span class="regexp">/(\^|\(|\)|\[|\]|\$|\*|\+|\.|\?|\\|\{|\}|\|)/g</span>, <span class="string">'\\$1'</span>);</span><br><span class="line"> <span class="comment">// 匹配字符决定是否显示当前option</span></span><br><span class="line"> <span class="keyword">this</span>.visible = <span class="keyword">new</span> <span class="built_in">RegExp</span>(parsedQuery, <span class="string">'i'</span>).test(<span class="keyword">this</span>.currentLabel) || <span class="keyword">this</span>.created;</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.visible) {</span><br><span class="line"> <span class="keyword">this</span>.select.filteredOptionsCount--;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>option 中通过正则匹配决定是否隐藏当前 option 组件,而 option-group 通过获取子组件,判断如果有子组件是可见的则显示,否则隐藏。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// option-group.vue</span></span><br><span class="line">queryChange() {</span><br><span class="line"> <span class="keyword">this</span>.visible = <span class="keyword">this</span>.$children &&</span><br><span class="line"> <span class="built_in">Array</span>.isArray(<span class="keyword">this</span>.$children) &&</span><br><span class="line"> <span class="keyword">this</span>.$children.some(<span class="function"><span class="params">option</span> =></span> option.visible === <span class="literal">true</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>所以,其实 option 和 option-group 在搜索的时候只是隐藏掉了不匹配的内容而已。</p><h2 id="下拉菜单的显示和隐藏效果是如何实现的?下拉菜单本质是什么东西?"><a href="#下拉菜单的显示和隐藏效果是如何实现的?下拉菜单本质是什么东西?" class="headerlink" title="下拉菜单的显示和隐藏效果是如何实现的?下拉菜单本质是什么东西?"></a>下拉菜单的显示和隐藏效果是如何实现的?下拉菜单本质是什么东西?</h2><p>下拉菜单是通过 <a href="https://cn.vuejs.org/v2/api/#transition" target="_blank" rel="noopener">transition</a> 来实现过渡动画的。<br>下拉菜单 <code>el-select-menu</code> 本质上就是一个 div 容器而已。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span></span></span><br><span class="line"><span class="tag"> <span class="attr">class</span>=<span class="string">"el-select-dropdown el-popper"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:class</span>=<span class="string">"[{ 'is-multiple': $parent.multiple }, popperClass]"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:style</span>=<span class="string">"{ minWidth: minWidth }"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">slot</span>></span><span class="tag"></<span class="name">slot</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></p><p>另外,在代码中经常出现的通知下拉菜单显示和隐藏的广播在 <code>el-select-menu</code> 的 <code>mounted</code> 方法中接收使用:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mounted() {</span><br><span class="line"> <span class="keyword">this</span>.referenceElm = <span class="keyword">this</span>.$parent.$refs.reference.$el;</span><br><span class="line"> <span class="keyword">this</span>.$parent.popperElm = <span class="keyword">this</span>.popperElm = <span class="keyword">this</span>.$el;</span><br><span class="line"> <span class="keyword">this</span>.$on(<span class="string">'updatePopper'</span>, () => {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.$parent.visible) <span class="keyword">this</span>.updatePopper();</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">this</span>.$on(<span class="string">'destroyPopper'</span>, <span class="keyword">this</span>.destroyPopper);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="创建条目如何实现?"><a href="#创建条目如何实现?" class="headerlink" title="创建条目如何实现?"></a>创建条目如何实现?</h2><p>上文中提到过,就是在 select 中默认藏了一条 option,当创建条目时显示这个 option 并显示创建内容。点击这个 option 就可以把创建的内容添加到显示结果的 input 上了。</p><h2 id="如何展示远程数据?"><a href="#如何展示远程数据?" class="headerlink" title="如何展示远程数据?"></a>如何展示远程数据?</h2><p>通过为 select 设置 <code>remote</code> 和 <code>remote-method</code> 属性来获取远程数据。<code>remote-method</code> 方法最终将数据赋值给 option 的 v-model 绑定数组数据将结果显示出来即可。</p><h2 id="清空按钮显示和点击事件呢?"><a href="#清空按钮显示和点击事件呢?" class="headerlink" title="清空按钮显示和点击事件呢?"></a>清空按钮显示和点击事件呢?</h2><p>在显示结果的 input 文本框中有一个 <code><i></code> 标签,用于显示图标。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- 用户显示清空和向下箭头 --></span></span><br><span class="line"><span class="tag"><<span class="name">i</span> <span class="attr">slot</span>=<span class="string">"suffix"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">:class</span>=<span class="string">"['el-select__caret', 'el-input__icon', 'el-icon-' + iconClass]"</span></span></span><br><span class="line"><span class="tag"> @<span class="attr">click</span>=<span class="string">"handleIconClick"</span></span></span><br><span class="line"><span class="tag">></span><span class="tag"></<span class="name">i</span>></span></span><br></pre></td></tr></table></figure></p><p>最终 input 右侧显示什么图标由 <code>iconClass</code> 决定,其中 <code>circle-close</code> 就是圆形查查,即清空按钮~<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">iconClass() {</span><br><span class="line"> <span class="keyword">let</span> criteria = <span class="keyword">this</span>.clearable &&</span><br><span class="line"> !<span class="keyword">this</span>.selectDisabled &&</span><br><span class="line"> <span class="keyword">this</span>.inputHovering &&</span><br><span class="line"> !<span class="keyword">this</span>.multiple &&</span><br><span class="line"> <span class="keyword">this</span>.value !== <span class="literal">undefined</span> &&</span><br><span class="line"> <span class="keyword">this</span>.value !== <span class="string">''</span>;</span><br><span class="line"> <span class="keyword">return</span> criteria ? <span class="string">'circle-close is-show-close'</span> : (<span class="keyword">this</span>.remote && <span class="keyword">this</span>.filterable ? <span class="string">''</span> : <span class="string">'arrow-up'</span>);</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p><code>handleIconClick</code> 方法:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 处理图标点击事件(删除按钮)</span></span><br><span class="line">handleIconClick(event) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.iconClass.indexOf(<span class="string">'circle-close'</span>) > <span class="number">-1</span>) {</span><br><span class="line"> <span class="keyword">this</span>.deleteSelected(event);</span><br><span class="line"> }</span><br><span class="line">},</span><br><span class="line"><span class="comment">// 删除选中</span></span><br><span class="line">deleteSelected(event) {</span><br><span class="line"> event.stopPropagation();</span><br><span class="line"> <span class="keyword">this</span>.$emit(<span class="string">'input'</span>, <span class="string">''</span>);</span><br><span class="line"> <span class="keyword">this</span>.emitChange(<span class="string">''</span>);</span><br><span class="line"> <span class="keyword">this</span>.visible = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">this</span>.$emit(<span class="string">'clear'</span>);</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><p>最终,清空只是将文本清空掉并且关闭下拉菜单。其实当再次打开 select 的时候,option 还是选中在之前选中的那个位置,即 <code>HoverIndex</code> 没有变为 -1,不知道算不算 bug。</p><h2 id="option-的自定义模板是如何实现的?"><a href="#option-的自定义模板是如何实现的?" class="headerlink" title="option 的自定义模板是如何实现的?"></a>option 的自定义模板是如何实现的?</h2><p>很简单,使用了 slot 插槽。并且在 slot 中定义了默认显示方式。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">slot</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span>></span>{{ currentLabel }}<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"></<span class="name">slot</span>></span></span><br></pre></td></tr></table></figure></p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>第一次尝试用问题取代主题来写博客,这样看着中心是不是更明确一些?<br>最后,说下看完 select 组件的感受:</p><ul><li>element 通过自定义的广播方法进行父子组件间的通信。(好像以前Vue也有这个功能,后来弃用了。)</li><li>再复杂的组件都是由一个个基础的组件拼起来的。</li><li>select 功能还是挺复杂的,加上子组件 1000+ 行代码了。本文只是讲了基本功能的实现,值得深入学习。</li><li>学习了高手写组件的方式和写法~之后在自己写组件的时候可以参考。</li><li>方法、参数命名非常规范,一眼就能看懂具体用法。</li><li>知道了 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some" target="_blank" rel="noopener">Array.some()</a> 方法~</li></ul><p>好吧,说好了一天写出来,结果断断续续花了三天才完成。有点高估自己能力啦~<br>说下之后的Vue实验室博客计划:计划再找两个复杂的 element 组件来学习,最后写一篇总结博客。然后试着自己去创建几个 UI 组件,学以致用。</p>]]></content>
<summary type="html">
<blockquote>
<p>select 选择器是个比较复杂的组件了,通过不同的配置可以有多种用法。有必要单独学习学习。</p>
</blockquote>
<h1 id="整体结构"><a href="#整体结构" class="headerlink" title="整体结
</summary>
<category term="element源码学习" scheme="https://violetjack.github.io/tags/element%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>HTTP 协议学习笔记(扫盲文)</title>
<link href="https://violetjack.github.io/2018/03/17/HTTP%20%E5%8D%8F%E8%AE%AE%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E6%89%AB%E7%9B%B2%E6%96%87%EF%BC%89/"/>
<id>https://violetjack.github.io/2018/03/17/HTTP 协议学习笔记(扫盲文)/</id>
<published>2018-03-16T16:00:00.000Z</published>
<updated>2019-02-15T17:20:56.251Z</updated>
<content type="html"><![CDATA[<blockquote><p>作为一个前端,了解下 HTTP 协议是很有必要的。</p></blockquote><p>先说个题外话,从《跃迁》一书中提到,高手获取信息的方式 —— 只获取一手、二手资料。<br>我在学习 HTTP 协议的时候犯了的毛病是 —— 百度了一堆 HTTP 相关文章,内容不一、质量参差不齐,看得云里雾里。最后干脆去Google查 HTTP,发现在 MDN 上有详细的 HTTP 相关资料,于是抛弃三手、四手资料,直接去看 MDN 上的内容。<br>所以学习知识,必须得先认清资料的来源和专业程度,在知识源头学习可以提高学习的效率和准确性。</p><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><blockquote><p><strong>HTTP是一种能够获取如 HTML 这样的网络资源的 </strong><a href="https://developer.mozilla.org/en-US/docs/Glossary/protocol" title="protocol: A protocol is a system of rules that define how data is exchanged within or between computers. Communications between devices require that the devices agree on the format of the data that is being exchanged. The set of rules that defines a format is called a protocol." target="_blank" rel="noopener">protocol</a>(通讯协议)。<strong>它是在 Web 上进行数据交换的基础,是一种 client-server 协议,也就是说,请求通常是由像浏览器这样的接受方发起的。一个完整的Web文档通常是由不同的子文档拼接而成的,像是文本、布局描述、图片、视频、脚本等等。</strong></p></blockquote><p>这里加上我个人的理解:HTTP 是一种在客户端和服务端之间传输信息的传输协议,或者说是一整套传输方案。所以,像 Headers、Cookie、Methods 等都是 HTTP 协议中的内容。用于更好的解决 client-server 传输信息中遇到的各种问题。</p><p>关于HTTP的更多历史信息,可以看到阮一峰老师的<a href="http://www.ruanyifeng.com/blog/2016/08/http.html" target="_blank" rel="noopener">HTTP 协议入门</a>(阮一峰老师写的文章还是那么清晰明了,向老师学习!)。</p><h1 id="TCP-IP-三次握手"><a href="#TCP-IP-三次握手" class="headerlink" title="TCP/IP 三次握手"></a>TCP/IP 三次握手</h1><p>HTTP 的传输协议是 TCP/IP 协议,该协议连接是需要进行三次握手的。<br>那么为什么必须是三次?</p><blockquote><p>这个问题的本质是:信道不可靠,但是通信双发需要就某个问题达成一致。而要解决这个问题,无论你在消息中包含什么信息,三次通信是理论上的最小值。所以三次握手不是TCP本身的要求,而是为了满足”在不可靠信道上可靠地传输信息”这一需求所导致的。</p></blockquote><p>以下列出三次握手具体步骤与理解:</p><ul><li>客户端发送 SYN 报文给服务器端,进入 SYN_SEND 状态。 —— 客户端向服务器端发送消息,请求它的回应。</li><li>服务器收到 SYN 报文,回应一个 SYN ACK 报文,进入 SYN_RECV 状态。 —— 服务器端收到消息,采取回应行为,回复消息告诉客户端收到了。</li><li>客户端收到服务器 SYN 报文,回应一个 ACK 报文,进入连接状态。 —— 客户端收到消息,这时客户端收到响应,表明发送的数据有回信了。但是服务器端发送了回应却还不知道客户端有没有收到。这时客户端需要再次发送消息告诉服务器我收到了。服务器收到消息后,客户端和服务器都知道对方已准备好通讯,然后就开始连接通讯了。</li></ul><h1 id="HTTP-的请求-request-和响应-response"><a href="#HTTP-的请求-request-和响应-response" class="headerlink" title="HTTP 的请求(request)和响应(response)"></a>HTTP 的请求(request)和响应(response)</h1><h2 id="请求"><a href="#请求" class="headerlink" title="请求"></a>请求</h2><p>请求为客户端发送给服务器的数据。具体有如下数据:</p><ul><li>请求方法:如 GET、 POST 这类请求方法。</li><li>要获取的资源路径。</li><li>HTTP协议版本号。</li><li>Headers:传递附加信息。</li><li>body:如果想 POST 请求,就会传递 body 资源数据给服务器。</li></ul><p><img src="https://upload-images.jianshu.io/upload_images/1987062-91b2abd5c2ae7063.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Request"></p><h2 id="响应"><a href="#响应" class="headerlink" title="响应"></a>响应</h2><p>响应为服务器收到客户端发送数据返回的数据,具体有如下数据:</p><ul><li>HTTP协议版本号。</li><li>状态码(status code):告知请求是否成功以及失败原因。</li><li>状态消息(status message):非权威状态码信息,由服务器自行设定。</li><li>Headers:传递附加信息。</li><li>body: 响应返回的资源存在body中。一般返回图片、HTML等资源。</li></ul><p><img src="https://upload-images.jianshu.io/upload_images/1987062-f554dd2fc23359f0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Response"></p><h1 id="Headers-头文件"><a href="#Headers-头文件" class="headerlink" title="Headers 头文件"></a>Headers 头文件</h1><p>头文件允许客户端和服务器通过请求和响应传递附加信息。<br>下面列出一些常用的消息头及其用法:</p><ul><li>Date 信息来源的日期时间</li><li>Content-Type 指定服务器文档的MIME类型,帮助用户代理去处理接收到的数据。</li><li>Content-Length 表示 body 的字节长度。</li><li>Host 服务器的域名。</li><li>User-Agent 可以用来识别发送请求的浏览器,是产品标记符和注释的清单。</li><li>Accept 用户代理期望的MIME类型列表</li><li>Accept-Encoding 列出用户代理支持的压缩方法</li><li>Accept-Ranges 期望范围。参数:byte、none。</li><li>Assess-Control-Allow-Origin 允许组织连接控制 。</li><li>Age 对象在代理缓存中的时间</li><li>Cache-Control 指定缓存机制</li><li>Connection 是否保持网络连接打开状态。参数:keep-alive、close。</li><li>ETag 特定版本资源标识符</li><li>Expires 过期时间日期</li><li>Server 服务器信息,如JSP、Apache等。</li><li>Referer 可用于识别用户访问位置</li></ul><p>还有一些自定专用消息头可通过 <code>X-</code> 前缀来添加。如 <code>x-oss-object-type</code>。</p><h1 id="状态码"><a href="#状态码" class="headerlink" title="状态码"></a>状态码</h1><p>状态码是由服务器端发送的响应中带有的请求结果的信息码。可以分为以下几类:</p><ul><li>1** 信息响应</li><li>2** 成功响应</li><li>3** 重定向</li><li>4** 客户端响应</li><li>5** 服务器响应</li></ul><p>列一些常见的状态返回码:</p><ul><li>200 请求成功。</li><li>304 未修改。</li><li>401 当前请求需要用户验证。</li><li>404 未找到资源。</li><li>500 服务器内部错误,无法完成请求。</li></ul><p>状态码其实只要了解常用的,冷门的遇到的时候查查 MDN 就好了。</p><h1 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h1><p>Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器发起请求时被懈怠并发送到服务器上。<br>HTTP 本质上无状态,使用 Cookie 可以创建有状态会话。服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据。所以,Cookie 常被用来验证用户登录信息。<br>更多 Cookie 内容请看 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies</a></p><h1 id="跨域"><a href="#跨域" class="headerlink" title="跨域"></a>跨域</h1><p>跨域就是获取不同域名下服务器的数据。跨域对于 GET 请求没有影响。对于其他请求,会先发送一个 OPTIONS 方法发送一个预检请求,获知服务端是否允许跨域请求。<br>所以,之前遇到 POST 方法请求变为 OPTIONS 请求报错的问题就是跨域问题。<br>在 Express 中也遇到个跨域问题,最终使用 <code>$ npm install cors</code> 的方式解决了。 </p><h1 id="HTTPS"><a href="#HTTPS" class="headerlink" title="HTTPS"></a>HTTPS</h1><h2 id="HTTP-和-HTTPS-的区别"><a href="#HTTP-和-HTTPS-的区别" class="headerlink" title="HTTP 和 HTTPS 的区别"></a>HTTP 和 HTTPS 的区别</h2><p>因为 HTTP 所封装的信息是明文的,通过抓包工具可以分析其信息内容。所以 HTTP 是及其不安全的。<br>而 HTTPS 在 HTTP 传输协议的基础上加上了 SSL/TLS 加密协议,所以传输的信息都是加密过的。不易被截获信息内容。所以 HTTPS 比 HTTP 安全性更高。</p><h2 id="HTTPS-运行机制"><a href="#HTTPS-运行机制" class="headerlink" title="HTTPS 运行机制"></a>HTTPS 运行机制</h2><p>大致运行步骤如下:</p><ol><li>客户端发起 HTTPS 请求</li><li>服务端获取数字证书CA —— 服务器向数字证书认证机构申请获取数字证书 CA 表明服务器是合法的、无害的。</li><li>传送数字证书 —— 将数字证书传给客户端</li><li>客户端解析证书 —— 客户端向数字证书认证机构查询,验证服务器合法性。</li><li>客户端传输加密后信息给服务端</li><li>服务端解密信息</li><li>服务端传输加密后的信息给客户端</li><li>客户端解密信息</li></ol><h2 id="常见加密算法"><a href="#常见加密算法" class="headerlink" title="常见加密算法"></a>常见加密算法</h2><p>非对称加密算法:RSA,DSA/DSS<br>对称加密算法:AES,RC4,3DES<br>HASH算法:MD5,SHA1,SHA256</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>发现网上写 HTTP 协议的文章很多,但是众口不一,每篇博客的内容都有所不同,搜集资料的时候还是蛮困惑的。<br>本文整理了一些 HTTP 的基础知识,写的不够详细。更加深入的学习通过查 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP" target="_blank" rel="noopener">MDN</a> 来解决问题啦~</p><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><ul><li>MDN <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/HTTP</a></li><li>通俗大白话来理解TCP协议的三次握手和四次分手<a href="https://github.com/jawil/blog/issues/14" target="_blank" rel="noopener">https://github.com/jawil/blog/issues/14</a></li><li>https原理:证书传递、验证和数据加密、解密过程解析<br><a href="http://blog.csdn.net/clh604/article/details/22179907" target="_blank" rel="noopener">http://blog.csdn.net/clh604/article/details/22179907</a></li></ul><h1 id="更新内容"><a href="#更新内容" class="headerlink" title="更新内容"></a>更新内容</h1><p>本文只是 HTTP 的扫盲文,并没有深入学习 HTTP 的意思,其实深入学习 HTTP 水还是很深的。换位思考,我感觉面试官问 HTTP 相关问题的主要目的是:<strong>能够很好与后端工程师沟通接口问题;更好处理前端工作中网络传输的问题;保证项目的信息安全;</strong>前端工程师作为客户端方的开发,也有必要和服务器端的后端工程师合作将程序开发的高效、安全、易维护。<br>–03-19更新–<br>我去问了一位前辈前端为什么要学习 HTTP 协议,大牛前辈的回答是这样的:“<strong>就前端来讲,两个用处吧:前端性能优化必须掌握,排障必须掌握</strong>”</p><p>感觉这篇文章内容实在少了点。所以又补充了点内容,贴上几条最靠谱的 HTTP 学习资料希望能够帮到一些想深入学习 HTTP 童鞋~</p><ul><li><a href="https://item.jd.com/11056556.html?dist=jd" target="_blank" rel="noopener">《HTTP权威指南》</a>(京东)</li><li><a href="https://zh.wikipedia.org/wiki/%E8%B6%85%E6%96%87%E6%9C%AC%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE" target="_blank" rel="noopener">超文本传输协议 —— 维基百科</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP" target="_blank" rel="noopener">HTTP | MDN</a></li></ul><p>记住文章开头所讲的,只去源头的知识学习才能保证学习的效率和准确性~祝我们能够更快更好的学习技术知识。</p>]]></content>
<summary type="html">
<blockquote>
<p>作为一个前端,了解下 HTTP 协议是很有必要的。</p>
</blockquote>
<p>先说个题外话,从《跃迁》一书中提到,高手获取信息的方式 —— 只获取一手、二手资料。<br>我在学习 HTTP 协议的时候犯了的毛病是 —— 百度了一堆
</summary>
</entry>
<entry>
<title>element 源码学习(番外篇) —— SASS 五分钟快速入门</title>
<link href="https://violetjack.github.io/2018/03/14/element-code-extra/"/>
<id>https://violetjack.github.io/2018/03/14/element-code-extra/</id>
<published>2018-03-13T16:00:00.000Z</published>
<updated>2018-03-21T05:33:03.986Z</updated>
<content type="html"><![CDATA[<blockquote><p>这算是 element 源码学习的番外篇,因为 element 中使用了大量 sass 来写样式。而 UI 框架的核心其实就是样式。所以,抽空把 sass 学了一遍,写了些小 demo 实践,总结成此文。</p></blockquote><h1 id="SASS-安装和调试"><a href="#SASS-安装和调试" class="headerlink" title="SASS 安装和调试"></a>SASS 安装和调试</h1><p>简单说下 sass 如何安装和编译调试。<br>参照<a href="http://sass-lang.com/install" target="_blank" rel="noopener">官网</a>,需要使用 gem 来安装 sass。如果是windows用户没有 gem 需要先安装 <a href="https://rubyinstaller.org/" target="_blank" rel="noopener">Ruby</a><br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> gem install sass</span><br></pre></td></tr></table></figure></p><p>如果有权限问题,需要加上 <code>sudo</code> 。<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> sudo gem install sass</span><br></pre></td></tr></table></figure></p><p>最后,通过查询 sass 版本号验证是否安装成功。<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> sass -v</span><br></pre></td></tr></table></figure></p><p>编译命令很简单,在项目目录下编译选中 <code>.scss</code>、<code>.sass</code> 文件即可。<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> sass hello.scss hello.css</span><br></pre></td></tr></table></figure></p><p>如果是学习 sass 这一个命令足矣,其他命令可参考<a href="https://www.w3cplus.com/sassguide/compile.html" target="_blank" rel="noopener">sass 编译</a></p><h1 id="语法简述"><a href="#语法简述" class="headerlink" title="语法简述"></a>语法简述</h1><p>下面我用自己对 sass 语法的理解,配合上 demo 快速过一遍 sass 基础语法。</p><h2 id="sass-文件和-scss-文件区别"><a href="#sass-文件和-scss-文件区别" class="headerlink" title="sass 文件和 scss 文件区别"></a>sass 文件和 scss 文件区别</h2><p>两者其实都是 sass 可以识别的文件,唯一不同点是 <code>.sass</code> 文件不使用大括号和分号。如下~<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">// .scss</span><br><span class="line">$default-color: #FFAACC;</span><br><span class="line">.selected {</span><br><span class="line"> color: $default-color;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// .sass</span><br><span class="line">$default-color: #FFAACC</span><br><span class="line">.selected </span><br><span class="line"> color: $default-color</span><br><span class="line"></span><br><span class="line">// .css</span><br><span class="line">.selected {</span><br><span class="line"> color: #FFAACC; </span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>官方文档推荐使用 <code>.scs</code>s 文件类型写法,避免 <code>.sass</code> 文件的严格格式要求报错。</p><h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><p>通过 <code>$</code> 符号来定义 sass 变量,变量在样式内外都可定义,用于各个样式中。定义的变量不会在编译后的 CSS 文件中显示。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$default-color: #FFAACC;</span><br><span class="line">$border-color: #AAFFCC;</span><br><span class="line">$default-border: 1px solid $border-color;</span><br><span class="line">$extra-color: #BBDD00;</span><br><span class="line"></span><br><span class="line">.selected {</span><br><span class="line"> $scoped-width: 60px;</span><br><span class="line"> width: $scoped-width;</span><br><span class="line"> color: $default-color;</span><br><span class="line"> border: $default-border;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>编译结果:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.selected</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">60px</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#FFAACC</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#AAFFCC</span>; }</span><br></pre></td></tr></table></figure></p><p>另外注意的一个点是在定义变量时使用的 <code>-</code> 和 <code>_</code> 的效果是一样的。即 <code>$border-color</code> 和 <code>$border_color</code> 指向的是同一个变量。</p><h2 id="嵌套"><a href="#嵌套" class="headerlink" title="嵌套"></a>嵌套</h2><p>为了避免一些代码的重复,引入了代码的嵌套。看一个例子:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">.selected {</span><br><span class="line"> color: #FFAA22;</span><br><span class="line"> h1 {</span><br><span class="line"> color: #FFDD77</span><br><span class="line"> }</span><br><span class="line"> div {</span><br><span class="line"> width: 50px;</span><br><span class="line"> height: 20px;</span><br><span class="line"> span {</span><br><span class="line"> color: #012DD6;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> &:hover {</span><br><span class="line"> color: #FAFAFA;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>得到的结果如下:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.selected</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#FFAA22</span>; }</span><br><span class="line"> <span class="selector-class">.selected</span> <span class="selector-tag">h1</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#FFDD77</span>; }</span><br><span class="line"> <span class="selector-class">.selected</span> <span class="selector-tag">div</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">20px</span>; }</span><br><span class="line"> <span class="selector-class">.selected</span> <span class="selector-tag">div</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#012DD6</span>; }</span><br><span class="line"> <span class="selector-class">.selected</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#FAFAFA</span>; }</span><br></pre></td></tr></table></figure></p><p>从中可以看到,嵌套可以将需要重复写选择器的过程嵌套到一个表达式中了。</p><h3 id="父选择器标识符-amp"><a href="#父选择器标识符-amp" class="headerlink" title="父选择器标识符 &"></a>父选择器标识符 &</h3><p>从上面的例子中看到有这么一段 <code>&:hover</code> 而在编译结果中得到的结果是 <code>.selected:hover</code> ,其实 <code>&</code> 标识符就代表了父级选择器。就这么简单,不理解的时候把父级选择题替换 <code>&</code> 理解下就简单了。</p><h3 id="嵌套css"><a href="#嵌套css" class="headerlink" title="嵌套css"></a>嵌套css</h3><p>sass 中的嵌套是可以多个样式同时嵌套的。 用法见demo。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.container .content {</span><br><span class="line"> h1, h2, h3 {margin-bottom: .8em}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>编译结果<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> <span class="selector-class">.content</span> <span class="selector-tag">h1</span>, <span class="selector-class">.container</span> <span class="selector-class">.content</span> <span class="selector-tag">h2</span>, <span class="selector-class">.container</span> <span class="selector-class">.content</span> <span class="selector-tag">h3</span> {</span><br><span class="line"> <span class="attribute">margin-bottom</span>: .<span class="number">8em</span>; }</span><br></pre></td></tr></table></figure></p><h3 id="子组合选择器和同层选择器"><a href="#子组合选择器和同层选择器" class="headerlink" title="子组合选择器和同层选择器"></a>子组合选择器和同层选择器</h3><p>关于 <code>></code>、<code>+</code> 和 <code>~</code> 这三个选择器,是 CSS3 中就有的。在 SASS 中同样适用。<br>简单说下三个选择器用途(参考<a href="http://www.w3school.com.cn/cssref/css_selectors.asp" target="_blank" rel="noopener"> CSS 选择器</a>):</p><ul><li><code>div>p</code> 选取父元素是 <div> 元素的每个 <p> 元素</p></div></li><li><code>div+p</code> 选择 <div> 元素之后紧跟的每个 <p> 元素</p></div></li><li><code>p~ul</code> 选择前面有 <p> 元素的每个 <ul> 元素。</ul></p></li></ul><p>看个官网的例子:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">article {</span><br><span class="line"> ~ article { border-top: 1px dashed #ccc }</span><br><span class="line"> > section { background: #eee }</span><br><span class="line"> dl > {</span><br><span class="line"> dt { color: #333 }</span><br><span class="line"> dd { color: #555 }</span><br><span class="line"> }</span><br><span class="line"> nav + & { margin-top: 0 }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>动手编译出的结果如下:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">article</span> ~ <span class="selector-tag">article</span> {</span><br><span class="line"> <span class="attribute">border-top</span>: <span class="number">1px</span> dashed <span class="number">#ccc</span>; }</span><br><span class="line"><span class="selector-tag">article</span> > <span class="selector-tag">section</span> {</span><br><span class="line"> <span class="attribute">background</span>: <span class="number">#eee</span>; }</span><br><span class="line"><span class="selector-tag">article</span> <span class="selector-tag">dl</span> > <span class="selector-tag">dt</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#333</span>; }</span><br><span class="line"><span class="selector-tag">article</span> <span class="selector-tag">dl</span> > <span class="selector-tag">dd</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#555</span>; }</span><br><span class="line"><span class="selector-tag">nav</span> + <span class="selector-tag">article</span> {</span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">0</span>; }</span><br></pre></td></tr></table></figure></p><h3 id="嵌套属性"><a href="#嵌套属性" class="headerlink" title="嵌套属性"></a>嵌套属性</h3><p>属性嵌套说白了就是把 <code>margin-bottom</code> 这类有 <code>-</code> 符号隔开的属性拆分开来便于查看和编写。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">nav {</span><br><span class="line"> border: {</span><br><span class="line"> style: solid;</span><br><span class="line"> width: 1px;</span><br><span class="line"> color: #ccc;</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></p><p>这里把 <code>border-style</code> 等属性进行了拆分,结果如下:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">nav</span> {</span><br><span class="line"> <span class="attribute">border-style</span>: solid;</span><br><span class="line"> <span class="attribute">border-width</span>: <span class="number">1px</span>;</span><br><span class="line"> <span class="attribute">border-color</span>: <span class="number">#ccc</span>; }</span><br></pre></td></tr></table></figure></p><h2 id="导入"><a href="#导入" class="headerlink" title="导入"></a>导入</h2><p>sass 提供了 sass 文件导入功能。可以做一些基础样式的复用。用法很简单:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">// var.scss</span><br><span class="line">$default-color: #AABBCC;</span><br><span class="line"></span><br><span class="line">.focused {</span><br><span class="line"> color: red;</span><br><span class="line"> margin: 5px;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// h1.scss</span><br><span class="line">h1 {</span><br><span class="line"> color: #BBDDFF;</span><br><span class="line"> margin: 10px;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// demo.scss</span><br><span class="line">@import "var01";</span><br><span class="line"></span><br><span class="line">$default-color: #FFAADD !default;</span><br><span class="line"></span><br><span class="line">.selected {</span><br><span class="line"> color: $default-color;</span><br><span class="line"> @import "h1";</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>以上代码中将 <code>var.scss</code> 和 <code>h1.scss</code> 两个文件导入到了 <code>demo.scss</code> 中,最终生成结果如下:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.focused</span> {</span><br><span class="line"> <span class="attribute">color</span>: red;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">5px</span>; }</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.selected</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#AABBCC</span>; }</span><br><span class="line"> <span class="selector-class">.selected</span> <span class="selector-tag">h1</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#BBDDFF</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">10px</span>; }</span><br></pre></td></tr></table></figure></p><p>从结果来说说导入的几个注意点:</p><ul><li>导入文件使用 <code>@import</code> 表达式来导入,导入可以是外部导入也可是嵌套导入。上面例子中 <code>var</code> 是外部导入,而 <code>h1</code> 是嵌套导入的。</li><li><code>$default-color: #FFAADD !default;</code> 中的 <code>!default</code> 是定义变量默认值的方式,如果导入文件中有同样的值优先使用导入的值,如果导入文件中没有这个值,使用默认值。</li><li>sass 中的 <code>@import</code> 与 css 中的 <code>@import</code> 不同,sass 中是编译的时候就直接导入生成 css 文件了,而 css 中,只有执行到 <code>@import</code> 时,浏览器才会去下载 css 文件,会导致页面加载变慢。</li></ul><h2 id="静默注释"><a href="#静默注释" class="headerlink" title="静默注释"></a>静默注释</h2><p>就是在 sass 是否保留注释内容的语法。保留注释的方式为:<strong>在CSS语法允许的地方,以 <code>/*...*/</code> 的方式写注释就能在生成的 css 文件中看到。</strong>再来看一个例子:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">// 不显示</span><br><span class="line">/* 显示 */</span><br><span class="line"></span><br><span class="line">.selected {</span><br><span class="line"> //不显示</span><br><span class="line"> /* 显示 */</span><br><span class="line"> color: #FFAADD; // 不显示</span><br><span class="line"> margin:/* 不显示 */ 10px; /* 显示 */</span><br><span class="line"> /* 显示 */border: 1px dashed /* 不显示 */ #ccc;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 不显示</span><br><span class="line">/* 显示 */</span><br></pre></td></tr></table></figure></p><p>编译结果正如注释所预测的。</p><h2 id="混合器"><a href="#混合器" class="headerlink" title="混合器"></a>混合器</h2><p>混合器在 element 的样式表中用的非常多,是个很强大的功能。混合器以 <code>@mixin</code> 来导出混合内容,使用 <code>@include</code> 来导入混合内容。</p><h3 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h3><p>我的理解是:<code>@mixin</code> 类似定义变量一样定义个混合器(编译的时候不显示 <code>@mixin</code> 的内容),<code>@include</code> 获取混合器来替换 <code>@include xxxx</code> 的这行内容。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">// mixin.scss</span><br><span class="line">@mixin rounded-corners {</span><br><span class="line"> -moz-border-radius: 5px;</span><br><span class="line"> -webkit-border-radius: 5px;</span><br><span class="line"> border-radius: 5px;</span><br><span class="line"> div {</span><br><span class="line"> width: 50px;</span><br><span class="line"> height: 20px;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">.name {</span><br><span class="line"> span {</span><br><span class="line"> color: #FFAADD;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// include.scss</span><br><span class="line">@import "mixin";</span><br><span class="line"></span><br><span class="line">.notice {</span><br><span class="line"> background-color: green;</span><br><span class="line"> border: 2px solid #00aa00;</span><br><span class="line"> @include rounded-corners;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>这里在官方 demo 上加了点代码验证问题。得到结果如下:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.name</span> <span class="selector-tag">span</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#FFAADD</span>; }</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.notice</span> {</span><br><span class="line"> <span class="attribute">background-color</span>: green;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">2px</span> solid <span class="number">#00aa00</span>;</span><br><span class="line"> <span class="attribute">-moz-border-radius</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">-webkit-border-radius</span>: <span class="number">5px</span>;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">5px</span>; }</span><br><span class="line"> <span class="selector-class">.notice</span> <span class="selector-tag">div</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">20px</span>; }</span><br></pre></td></tr></table></figure></p><p>这个demo验证了:1. <code>@mixin</code> 是一个类似变量的内容。在 <code>@import</code> 导入的内容中只显示了 <code>.name</code> 样式。 2. <code>@include</code> 会替换 <code>@include xxx</code> 这段代码。就算 <code>@mixin</code> 中有各种写法都会应用到 <code>@include</code>中,如嵌套 CSS。</p><h3 id="混合器传参"><a href="#混合器传参" class="headerlink" title="混合器传参"></a>混合器传参</h3><p>混合器可以接收 <code>@include</code> 表达式传递的参数。而且,参数可以设置默认值。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">// mixin.scss</span><br><span class="line">@mixin link-colors($normal: white, $hover: white, $visited: white) {</span><br><span class="line"> color: $normal;</span><br><span class="line"> &:hover { color: $hover; }</span><br><span class="line"> &:visited { color: $visited; }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// include.scss</span><br><span class="line">@import "mixin";</span><br><span class="line"></span><br><span class="line">// 写法一,按照默认顺序传递参数</span><br><span class="line">a {</span><br><span class="line"> @include link-colors(blue, red, green);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 写法二,按照参数名传递参数</span><br><span class="line">b {</span><br><span class="line"> @include link-colors(</span><br><span class="line"> $normal: blue,</span><br><span class="line"> $visited: green,</span><br><span class="line"> $hover: red</span><br><span class="line"> );</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>编译结果为:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">a</span> {</span><br><span class="line"> <span class="attribute">color</span>: blue; }</span><br><span class="line"> <span class="selector-tag">a</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">color</span>: red; }</span><br><span class="line"> <span class="selector-tag">a</span><span class="selector-pseudo">:visited</span> {</span><br><span class="line"> <span class="attribute">color</span>: green; }</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">b</span> {</span><br><span class="line"> <span class="attribute">color</span>: blue; }</span><br><span class="line"> <span class="selector-tag">b</span><span class="selector-pseudo">:hover</span> {</span><br><span class="line"> <span class="attribute">color</span>: red; }</span><br><span class="line"> <span class="selector-tag">b</span><span class="selector-pseudo">:visited</span> {</span><br><span class="line"> <span class="attribute">color</span>: green; }</span><br></pre></td></tr></table></figure></p><p>如果说 <code>@include</code> 中不传递参数 <code>@include link-colors();</code> ,那么生成结果的 color 都为默认值 white。</p><h3 id="element-中的混合"><a href="#element-中的混合" class="headerlink" title="element 中的混合"></a>element 中的混合</h3><p>在 element 源码中用了不少混合,有一种写法 sass 的快速入门中没有提到。就找一个简单的 el-card 样式来学习下来:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">@import "mixins/mixins";</span><br><span class="line">@import "common/var";</span><br><span class="line"></span><br><span class="line">@include b(card) {</span><br><span class="line"> border-radius: $--card-border-radius;</span><br><span class="line"> border: 1px solid $--card-border-color;</span><br><span class="line"> background-color: $--color-white;</span><br><span class="line"> overflow: hidden;</span><br><span class="line"> box-shadow: $--box-shadow-light;</span><br><span class="line"> color: $--color-text-primary;</span><br><span class="line"></span><br><span class="line"> @include e(header) {</span><br><span class="line"> padding: #{$--card-padding - 2 $--card-padding};</span><br><span class="line"> border-bottom: 1px solid $--card-border-color;</span><br><span class="line"> box-sizing: border-box;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @include e(body) {</span><br><span class="line"> padding: $--card-padding;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>从中可以看到所有的属性都是使用了 <code>@include</code> 方式进行混合的。最终生成的 CSS 文件如下:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.el-card</span> {</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">4px</span>;</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#ebeef5</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#fff</span>;</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line"> <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">2px</span> <span class="number">12px</span> <span class="number">0</span> <span class="built_in">rgba</span>(0, 0, 0, 0.1);</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#303133</span>; }</span><br><span class="line"> <span class="selector-class">.el-card__header</span> {</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">18px</span> <span class="number">20px</span>;</span><br><span class="line"> <span class="attribute">border-bottom</span>: <span class="number">1px</span> solid <span class="number">#ebeef5</span>;</span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box; }</span><br><span class="line"> <span class="selector-class">.el-card__body</span> {</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">20px</span>; }</span><br></pre></td></tr></table></figure></p><p>先看下导入,其中 <code>var</code> 文件是项目样式变量统一保存的地方;<code>mixin</code> 文件用于混合;再加上几个 <code>@include</code> 表达式,答案一定是在 <code>mixin</code> 中的。我们就直接找到 <code>@mixin b</code> 和 <code>@mixin e</code> 两个混合项。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">@mixin b($block) {</span><br><span class="line"> $B: $namespace+'-'+$block !global; // 定义 B 变量:变量名 el-card</span><br><span class="line"></span><br><span class="line"> .#{$B} {</span><br><span class="line"> @content;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">@mixin e($element) {</span><br><span class="line"> $E: $element !global;</span><br><span class="line"> $selector: &;</span><br><span class="line"> $currentSelector: "";</span><br><span class="line"> @each $unit in $element {</span><br><span class="line"> $currentSelector: #{$currentSelector + "." + $B + $element-separator + $unit + ","};</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @if hitAllSpecialNestRule($selector) {</span><br><span class="line"> @at-root {</span><br><span class="line"> #{$selector} {</span><br><span class="line"> #{$currentSelector} {</span><br><span class="line"> @content;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } @else {</span><br><span class="line"> @at-root {</span><br><span class="line"> #{$currentSelector} {</span><br><span class="line"> @content;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>发现有许多 sass 快速入门中没有提到过的语法: <code>@if</code>,<code>@else</code> 等等,这里查阅具体文档列出其功能:</p><ul><li>@if @else 这两者和任何编程语言的 if … else … 的用法是一样的,条件判断。if 中条件为 true 进入逻辑,否则使用 else 逻辑。</li><li>@at-root <a href="https://www.sasscss.com/docs/#at-root" target="_blank" rel="noopener">@at-root</a> 指令导致一个或多个规则被限定输出在文档的根层级上,而不是被嵌套在其父选择器下。</li><li>@content 样式内容块可以传递到混入(mixin)包含样式的位置。样式内容块将出现在混入内的任何 @content 指令的位置。这使得可以定义抽象 关联到选择器和指令的解析。</li><li>@each in 类似js用法,遍历列表获取每个value值。</li><li><code>#{...}</code> 是<a href="https://www.sasscss.com/docs/#interpolation_" target="_blank" rel="noopener">插值语法</a>,用于在选择器和属性名中使用 SassScript 变量,所以 <code>.#{$B}</code> 表达式,如果 <code>$B</code> 的值为 hello-world,那么表达式结果等于 <code>.hello-world</code></li></ul><p>其实看完这些用法,上面的代码就很好理解了。具体关于 element 样式学习的细节将在下篇博客中详细学习。</p><h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>个人感觉继承就是几个样式类写在一起。而且,继承是可以嵌套的。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">.error {</span><br><span class="line"> border: 1px red;</span><br><span class="line"> background-color: #fdd;</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line">.seriousError {</span><br><span class="line"> @extend .error;</span><br><span class="line"> border-width: 3px;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">.error02 {</span><br><span class="line"> @extend .seriousError;</span><br><span class="line"> margin: 5px;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>编译结果为:<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.error</span>, <span class="selector-class">.seriousError</span>, <span class="selector-class">.error02</span> {</span><br><span class="line"> <span class="attribute">border</span>: <span class="number">1px</span> red;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#fdd</span>; }</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.seriousError</span>, <span class="selector-class">.error02</span> {</span><br><span class="line"> <span class="attribute">border-width</span>: <span class="number">3px</span>; }</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.error02</span> {</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">5px</span>; }</span><br></pre></td></tr></table></figure></p><p>下面引用下继承的注意事项:</p><blockquote><ul><li>跟混合器相比,继承生成的 css 代码相对更少。因为继承仅仅是重复选择器,而不会重复属性,所以使用继承往往比混合器生成的 css 体积更小。如果你非常关心你站点的速度,请牢记这一点。</li><li>继承遵从 css 层叠的规则。当两个不同的 css 规则应用到同一个 html 元素上时,并且这两个不同的 css 规则对同一属性的修饰存在不同的值,css 层叠规则会决定应用哪个样式。相当直观:通常权重更高的选择器胜出,如果权重相同,定义在后边的规则胜出。</li></ul></blockquote><p>所以,其实继承相比混合更简单。继承只是选择器的重复,而混合是用一段代码替换标签 <code>@include</code> 标签。</p><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>由于 element 项目中使用了大量的 sass 样式,所以想了解 element 必须对 sass 有一定了解。本文简单解决了 sass 是什么?基础用法怎么用?两个问题。更加深入的 sass 语法涉及的不多,算是快速入门博客啦。<br>在了解了 sass,能够看懂 element 中的样式表后,就可以愉快的去学习 element 源码啦~</p>]]></content>
<summary type="html">
<blockquote>
<p>这算是 element 源码学习的番外篇,因为 element 中使用了大量 sass 来写样式。而 UI 框架的核心其实就是样式。所以,抽空把 sass 学了一遍,写了些小 demo 实践,总结成此文。</p>
</blockquote>
<h1
</summary>
<category term="element源码学习" scheme="https://violetjack.github.io/tags/element%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
</entry>
</feed>