-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
547 lines (280 loc) · 391 KB
/
Copy pathatom.xml
File metadata and controls
547 lines (280 loc) · 391 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
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>大枫</title>
<subtitle>Stay Hungry.Stay Foolish.</subtitle>
<link href="https://yongmaple.com/atom.xml" rel="self"/>
<link href="https://yongmaple.com/"/>
<updated>2021-08-29T13:38:44.140Z</updated>
<id>https://yongmaple.com/</id>
<author>
<name>YongMaple</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>CocoaPods换源</title>
<link href="https://yongmaple.com/2021/08/29/CocoaPods%E6%8D%A2%E6%BA%90/"/>
<id>https://yongmaple.com/2021/08/29/CocoaPods%E6%8D%A2%E6%BA%90/</id>
<published>2021-08-29T18:40:10.000Z</published>
<updated>2021-08-29T13:38:44.140Z</updated>
<content type="html"><![CDATA[<p>原文地址:<a href="https://www.jianshu.com/p/68a3bc2a41fc">https://www.jianshu.com/p/68a3bc2a41fc</a></p><p>只添加<code>source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'</code>就行了</p>]]></content>
<summary type="html"><p>原文地址:<a href="https://www.jianshu.com/p/68a3bc2a41fc">https://www.jianshu.com/p/68a3bc2a41fc</a></p>
<p>只添加<code>source &#39;https://mirr</summary>
<category term="报错" scheme="https://yongmaple.com/categories/%E6%8A%A5%E9%94%99/"/>
<category term="报错" scheme="https://yongmaple.com/tags/%E6%8A%A5%E9%94%99/"/>
</entry>
<entry>
<title>install报错</title>
<link href="https://yongmaple.com/2021/08/29/pod%20install%E6%8A%A5%E9%94%99/"/>
<id>https://yongmaple.com/2021/08/29/pod%20install%E6%8A%A5%E9%94%99/</id>
<published>2021-08-29T18:22:26.000Z</published>
<updated>2021-08-29T13:38:44.224Z</updated>
<content type="html"><![CDATA[<h3 id="pod-install时报443错误"><a href="#pod-install时报443错误" class="headerlink" title="pod install时报443错误"></a>pod install时报443错误</h3><ul><li>上<a href="https://www.ipaddress.com/ip-lookup">https://www.ipaddress.com/ip-lookup</a>搜索<code>github.com</code>获取到IP</li><li><code>sudo vim /etc/hosts</code></li><li>添加<code>140.82.113.4 github.com</code>IP地址改为刚才搜索到的IP</li></ul>]]></content>
<summary type="html"><h3 id="pod-install时报443错误"><a href="#pod-install时报443错误" class="headerlink" title="pod install时报443错误"></a>pod install时报443错误</h3><ul>
<li></summary>
<category term="报错" scheme="https://yongmaple.com/categories/%E6%8A%A5%E9%94%99/"/>
<category term="报错" scheme="https://yongmaple.com/tags/%E6%8A%A5%E9%94%99/"/>
</entry>
<entry>
<title>React组件化</title>
<link href="https://yongmaple.com/2021/07/10/React%E7%BB%84%E4%BB%B6%E5%8C%96/"/>
<id>https://yongmaple.com/2021/07/10/React%E7%BB%84%E4%BB%B6%E5%8C%96/</id>
<published>2021-07-10T18:20:14.000Z</published>
<updated>2021-08-29T13:38:44.140Z</updated>
<content type="html"><![CDATA[<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></pre></td><td class="code"><pre><span class="line">npx create-react-app xxx</span><br><span class="line">cd xxx</span><br><span class="line">yarn start</span><br></pre></td></tr></table></figure><h3 id="组件化优点"><a href="#组件化优点" class="headerlink" title="组件化优点"></a>组件化优点</h3><ol><li>增强代码重⽤性,提⾼开发效率</li><li>简化调试步骤,提升整个项⽬的可维护性</li><li>便于协同开发</li><li>注意点:降低耦合性</li></ol><h3 id="组件跨层级通信"><a href="#组件跨层级通信" class="headerlink" title="组件跨层级通信"></a>组件跨层级通信</h3><p><img src="/2021/07/10/React%E7%BB%84%E4%BB%B6%E5%8C%96/1.png"></p><p>在⼀个典型的 React 应⽤中,数据是通过 props 属性⾃上⽽下(由⽗及⼦)进⾏传递的,但这种做法对<br>于某些类型的属性⽽⾔是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应⽤程序中许多组件都<br>需要的。Context 提供了⼀种在组件之间共享此类值的⽅式,⽽不必显式地通过组件树的逐层传递<br>props。</p><p>React 中使⽤ Context 实现祖代组件向后代组件跨层级传值。Vue 中的 provide & inject 来源于 Context。</p><h4 id="Context-API"><a href="#Context-API" class="headerlink" title="Context API"></a>Context API</h4><h5 id="React-createContext"><a href="#React-createContext" class="headerlink" title="React.createContext"></a>React.createContext</h5><p>创建⼀个 Context 对象。当 React 渲染⼀个订阅了这个 Context 对象的组件,这个组件会从组件树中离<br>⾃身最近的那个匹配的 Provider 中读取到当前的 context 值。</p><h4 id="Context-Provider"><a href="#Context-Provider" class="headerlink" title="Context.Provider"></a>Context.Provider</h4><p>Provider 接收⼀个 value 属性,传递给消费组件,允许消费组件订阅 context 的变化。⼀个 Provider<br>可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使⽤,⾥层的会覆盖外层的数据。</p><p>当 Provider 的 value 值发⽣变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部<br>consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退<br>出更新的情况下也能更新。</p><h5 id="Class-contextType"><a href="#Class-contextType" class="headerlink" title="Class.contextType"></a>Class.contextType</h5><p>挂载在 class 上的 contextType 属性会被重赋值为⼀个由 <code>React.createContext()</code> 创建的 Context<br>对象。这能让你使⽤ this.context 来消费最近 Context 上的那个值。你可以在任何⽣命周期中访问<br>到它,包括 render 函数中。==只能用在类组件当中,只能订阅单一 context==</p><h5 id="Context-Consumer"><a href="#Context-Consumer" class="headerlink" title="Context.Consumer"></a>Context.Consumer</h5><p>这⾥,React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context。<br>这个函数接收当前的 context 值,返回⼀个 React 节点。传递给函数的 value 值等同于往上组件树离<br>这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider, value 参数等同于传递<br>给 createContext() 的 defaultValue 。</p><h5 id="useContext"><a href="#useContext" class="headerlink" title="useContext"></a>useContext</h5><p>接收⼀个 context 对象( React.createContext 的返回值)并返回该 context 的当前值。当前的<br>context 值由上层组件中距离当前组件最近的 <code><MyContext.Provider></code> 的 value prop 决定。==只能⽤<br>在 function 组件中==</p><h4 id="使⽤-Context"><a href="#使⽤-Context" class="headerlink" title="使⽤ Context"></a>使⽤ Context</h4><p>创建 Context => 获取 Provider 和 Consumer => Provider 提供值 => Consumer 消费值<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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> ContextTypePage <span class="keyword">from</span> <span class="string">'./ContextTypePage'</span></span><br><span class="line"><span class="keyword">import</span> { ThemeContext, UserContext } <span class="keyword">from</span> <span class="string">'../Context'</span></span><br><span class="line"><span class="keyword">import</span> UseContextPage <span class="keyword">from</span> <span class="string">'./UseContextPage'</span></span><br><span class="line"><span class="keyword">import</span> ConsumerPage <span class="keyword">from</span> <span class="string">'./ConsumerPage'</span></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">ContextPage</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> <span class="function"><span class="title">constructor</span>(<span class="params">props</span>)</span> {</span><br><span class="line"> <span class="built_in">super</span>(props)</span><br><span class="line"> <span class="built_in">this</span>.state = {</span><br><span class="line"> theme: {</span><br><span class="line"> themeColor: <span class="string">'red'</span>,</span><br><span class="line"> },</span><br><span class="line"> user: {</span><br><span class="line"> name: <span class="string">'xiaoming'</span>,</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> changeColor = <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">const</span> { themeColor } = <span class="built_in">this</span>.state.theme</span><br><span class="line"> <span class="built_in">this</span>.setState({</span><br><span class="line"> theme: {</span><br><span class="line"> themeColor: themeColor === <span class="string">'red'</span> ? <span class="string">'green'</span> : <span class="string">'red'</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="title">render</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">const</span> { theme, user } = <span class="built_in">this</span>.state</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div></span><br><span class="line"> <h3>ContextPage</h3></span><br><span class="line"> <button onClick={<span class="built_in">this</span>.changeColor}>change color</button></span><br><span class="line"> <ThemeContext.Provider value={theme}></span><br><span class="line"> <ContextTypePage /></span><br><span class="line"> <UserContext.Provider value={user}></span><br><span class="line"> <UseContextPage /></span><br><span class="line"> <ConsumerPage /></span><br><span class="line"> </UserContext.Provider></span><br><span class="line"> </ThemeContext.Provider></span><br><span class="line"> <ContextTypePage /></span><br><span class="line"> </div></span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Context.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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> ThemeContext = React.createContext({ <span class="attr">themeColor</span>: <span class="string">'pink'</span> })</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> UserContext = React.createContext()</span><br></pre></td></tr></table></figure><p>pages/ContextTypePage.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><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="keyword">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> { ThemeContext } <span class="keyword">from</span> <span class="string">'../Context'</span></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">ContextTypePage</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">static</span> contextType = ThemeContext</span><br><span class="line"> <span class="function"><span class="title">render</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">const</span> { themeColor } = <span class="built_in">this</span>.context</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div className=<span class="string">"border"</span>></span><br><span class="line"> <h3 className={themeColor}>ContextTypePage</h3></span><br><span class="line"> </div></span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>pages/ConsumerPage.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><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">import</span> React, { Component } <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> { ThemeContext, UserContext } <span class="keyword">from</span> <span class="string">'../Context'</span></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">ConsumerPage</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</span><br><span class="line"> <span class="function"><span class="title">render</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div className=<span class="string">"border"</span>></span><br><span class="line"> <ThemeContext.Consumer></span><br><span class="line"> {<span class="function">(<span class="params">themeContext</span>) =></span> (</span><br><span class="line"> <></span><br><span class="line"> <h3 className={themeContext.themeColor}>ConsumerPage</h3></span><br><span class="line"> <UserContext.Consumer></span><br><span class="line"> {<span class="function">(<span class="params">userContext</span>) =></span> <span class="xml"><span class="tag"><<span class="name">HandleUserContext</span> {<span class="attr">...userContext</span>} /></span></span>}</span><br><span class="line"> </UserContext.Consumer></span><br><span class="line"> </></span><br><span class="line"> )}</span><br><span class="line"> </ThemeContext.Consumer></span><br><span class="line"> </div></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">HandleUserContext</span>(<span class="params">userCtx</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag"><<span class="name">div</span>></span>{userCtx.name}<span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>消费多个 Context</p><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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ThemeProvider</span> <span class="attr">value</span>=<span class="string">"{theme}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ContextTypePage</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">ConsumerPage</span> /></span></span><br><span class="line"> {/*多个Context */}</span><br><span class="line"> <span class="tag"><<span class="name">UserProvider</span> <span class="attr">value</span>=<span class="string">"{user}"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">MultipleContextsPage</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">UserProvider</span>></span></span><br><span class="line"><span class="tag"></<span class="name">ThemeProvider</span>></span></span><br></pre></td></tr></table></figure><p>如果两个或者更多的 context 值经常被⼀起使⽤,那你可能要考虑⼀下另外创建你⾃⼰的渲染组件,以<br>提供这些值。</p><p>pages/UseContextPage</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="keyword">import</span> React, { useState, useEffect, useContext } <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> { ThemeContext, UserContext } <span class="keyword">from</span> <span class="string">'../Context'</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">UseContextPage</span>(<span class="params">props</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> themeContext = useContext(ThemeContext)</span><br><span class="line"> <span class="keyword">const</span> { themeColor } = themeContext</span><br><span class="line"> <span class="keyword">const</span> userContext = useContext(UserContext)</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div className=<span class="string">"border"</span>></span><br><span class="line"> <h3 className={themeColor}>UseContextPage</h3></span><br><span class="line"> <p>{userContext.name}</p></span><br><span class="line"> </div></span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><p>因为 context 会使⽤参考标识(reference identity)来决定何时进⾏渲染,这⾥可能会有⼀些陷阱,当<br>provider 的⽗组件进⾏重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例⼦,当每⼀次<br>Provider 重渲染时,以下的代码会重渲染所有下⾯的 consumers 组件,因为 value 属性总是被赋值<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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> <span class="function"><span class="title">render</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <Provider value={{ <span class="attr">something</span>: <span class="string">'something'</span> }}></span><br><span class="line"> <Toolbar /></span><br><span class="line"> </Provider></span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>为了防⽌这种情况,将 value 状态提升到⽗节点的 state ⾥:</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="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> <span class="function"><span class="title">constructor</span>(<span class="params">props</span>)</span> {</span><br><span class="line"> <span class="built_in">super</span>(props)</span><br><span class="line"> <span class="built_in">this</span>.state = {</span><br><span class="line"> value: { <span class="attr">something</span>: <span class="string">'something'</span> },</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="title">render</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <Provider value={<span class="built_in">this</span>.state.value}></span><br><span class="line"> <Toolbar /></span><br><span class="line"> </Provider></span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>在 React 的官⽅⽂档中, Context 被归类为⾼级部分(Advanced),属于 React 的⾼级 API,建议不要滥<br>⽤。<br>后⾯我们要学习到的 react-redux 的 <Provider> ,就是通过 Context 提供⼀个全局态的 store ,路<br>由组件 react-router 通过 Context 管理路由状态等等。在 React 组件开发中,如果⽤好 Context ,可以<br>让你的组件变得强⼤,⽽且灵活。</Provider></p>]]></content>
<summary type="html"><h3 id="快速开始"><a href="#快速开始" class="headerlink" title="快速开始"></a>快速开始</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="React" scheme="https://yongmaple.com/tags/React/"/>
<category term="手撕源码" scheme="https://yongmaple.com/tags/%E6%89%8B%E6%92%95%E6%BA%90%E7%A0%81/"/>
</entry>
<entry>
<title>Vue3源码解析三</title>
<link href="https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/"/>
<id>https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/</id>
<published>2021-05-31T16:45:59.000Z</published>
<updated>2021-08-29T13:38:44.204Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li><li><a href="https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/">Vue2 源码解析二</a></li><li><a href="https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/">Vue2 源码解析三</a></li></ul><p>本文项目地址:<a href="https://github.com/YongMaple/vue-next">https://github.com/YongMaple/vue-next</a></p>]]></content>
<summary type="html"><p>目录:</p>
<ul>
<li><a href="https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li>
<li><a href="htt</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="手撕源码" scheme="https://yongmaple.com/tags/%E6%89%8B%E6%92%95%E6%BA%90%E7%A0%81/"/>
<category term="Vue3" scheme="https://yongmaple.com/tags/Vue3/"/>
</entry>
<entry>
<title>Vue3源码解析二</title>
<link href="https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/"/>
<id>https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/</id>
<published>2021-05-31T16:45:54.000Z</published>
<updated>2021-08-29T13:38:44.204Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li><li><a href="https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/">Vue2 源码解析二</a></li><li><a href="https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/">Vue2 源码解析三</a></li></ul><p>本文项目地址:<a href="https://github.com/YongMaple/vue-next">https://github.com/YongMaple/vue-next</a></p><h3 id="体验-compositionAPI"><a href="#体验-compositionAPI" class="headerlink" title="体验 compositionAPI"></a>体验 compositionAPI</h3><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></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">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</span>></span></span><br><span class="line"> <span class="comment"><!-- <h3>{{state.title}}</h3> --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>{{foo}}<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="comment"><!-- 模板中自动解包 --></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title2}}<span class="tag"></<span class="name">h3</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">script</span> <span class="attr">src</span>=<span class="string">"http://unpkg.com/vue@next"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp, reactive, ref, toRefs } = Vue</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = createApp({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">setup</span>(<span class="params"></span>)</span> {</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">const</span> state = reactive({</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'vue3, hello'</span>,</span></span><br><span class="line"><span class="javascript"> <span class="comment">// props</span></span></span><br><span class="line"><span class="javascript"> foo: <span class="string">'foooooo'</span>,</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> state.title = <span class="string">'vue3, hello!!!!!'</span></span></span><br><span class="line"> }, 1000)</span><br><span class="line"><span class="javascript"> <span class="comment">// 单值响应</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// ref => Ref 包装类型</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> title2 = ref(<span class="string">'hello, vue3'</span>)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> title2.value = <span class="string">'hello, vue3!!!!!!'</span></span></span><br><span class="line"> }, 1000)</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"> ...toRefs(state),</span><br><span class="line"> title2,</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> app.mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>reactive 和 ref 两种方式中,相对更加推荐 ref</p><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></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">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</span>></span></span><br><span class="line"> <span class="comment"><!-- <h3>{{state.title}}</h3> --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>{{foo}}<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> @<span class="attr">click</span>=<span class="string">"add"</span>></span>{{count}}<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>{{double}}<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="comment"><!-- 模板中自动解包 --></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title2}}<span class="tag"></<span class="name">h3</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">script</span> <span class="attr">src</span>=<span class="string">"http://unpkg.com/vue@next"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp, reactive, ref, toRefs, computed, onMounted } = Vue</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">useState</span>(<span class="params"></span>) </span>{</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">const</span> state = reactive({</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'vue3, hello'</span>,</span></span><br><span class="line"><span class="javascript"> <span class="comment">// props</span></span></span><br><span class="line"><span class="javascript"> foo: <span class="string">'foooooo'</span>,</span></span><br><span class="line"> count: 1,</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> state.title = <span class="string">'vue3, hello!!!!!'</span></span></span><br><span class="line"> }, 3000)</span><br><span class="line"></span><br><span class="line"><span class="javascript"> onMounted(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> state.foo = <span class="string">'foo'</span></span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="comment">// 计算属性</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> double = computed(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> state.count * <span class="number">2</span></span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"> state.count++</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"> state,</span><br><span class="line"> double,</span><br><span class="line"> add,</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = createApp({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">setup</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { state, double, add } = useState()</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="comment">// 单值响应</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// ref => Ref 包装类型</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> title2 = ref(<span class="string">'hello, vue3'</span>)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> title2.value = <span class="string">'hello, vue3!!!!!!'</span></span></span><br><span class="line"> }, 3000)</span><br><span class="line"></span><br><span class="line"><span class="javascript"> onMounted(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> title2.value = <span class="string">'lalalaalal'</span></span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"> ...toRefs(state),</span><br><span class="line"> title2,</span><br><span class="line"> double,</span><br><span class="line"> add,</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> app.mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>下面是官方例子:</p><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></pre></td><td class="code"><pre><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">script</span> <span class="attr">src</span>=<span class="string">"../dist/vue.global.js"</span>></span><span class="tag"></<span class="name">script</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">h1</span>></span>logic reuse<span class="tag"></<span class="name">h1</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">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp, reactive, onMounted, onUnmounted, toRefs } = Vue</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">useMouse</span>(<span class="params"></span>) </span>{</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">const</span> state = reactive({</span></span><br><span class="line"> x: 0,</span><br><span class="line"> y: 0,</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> update = <span class="function">(<span class="params">e</span>) =></span> {</span></span><br><span class="line"> state.x = e.pageX</span><br><span class="line"> state.y = e.pageY</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> onMounted(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">window</span>.addEventListener(<span class="string">'mousemove'</span>, update)</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> onUnmounted(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">window</span>.removeEventListener(<span class="string">'mousemove'</span>, update)</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="comment">// 转换所有key为响应式数据</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> toRefs(state)</span></span><br><span class="line"> }</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">useTime</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> state = reactive({</span></span><br><span class="line"><span class="javascript"> time: <span class="keyword">new</span> <span class="built_in">Date</span>(),</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> onMounted(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">setInterval</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> state.time = <span class="keyword">new</span> <span class="built_in">Date</span>()</span></span><br><span class="line"> }, 1000)</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> toRefs(state)</span></span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="comment">// 逻辑组合</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> MyComp = {</span></span><br><span class="line"> template: `</span><br><span class="line"><span class="handlebars"><span class="xml"> <span class="tag"><<span class="name">div</span>></span>x: </span><span class="template-variable">{{ <span class="name">x</span> }}</span><span class="xml"> y: </span><span class="template-variable">{{ <span class="name">y</span> }}</span><span class="xml"><span class="tag"></<span class="name">div</span>></span></span></span></span><br><span class="line"><span class="handlebars"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>time: </span><span class="template-variable">{{<span class="name">time</span>}}</span><span class="xml"><span class="tag"></<span class="name">p</span>></span></span></span></span><br><span class="line"> `,</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">setup</span>(<span class="params"></span>)</span> {</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">const</span> { x, y } = useMouse()</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">const</span> { time } = useTime()</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">return</span> {</span></span><br><span class="line"> x,</span><br><span class="line"> y,</span><br><span class="line"> time,</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> createApp(MyComp).mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>对⽐ mixins,好处显⽽易⻅:</p><ul><li>x,y,time 来源清晰</li><li>不会与 data、props 等命名冲突</li><li>更好的维护性</li></ul><h3 id="响应式原理"><a href="#响应式原理" class="headerlink" title="响应式原理"></a>响应式原理</h3><h4 id="Vue2-的方式"><a href="#Vue2-的方式" class="headerlink" title="Vue2 的方式"></a>Vue2 的方式</h4><p>Object.defineProperty()</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 拦截每个key,从⽽可以侦测数据变化</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">defineReactive</span>(<span class="params">obj, key, val</span>) </span>{</span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(obj, key, {</span><br><span class="line"> <span class="function"><span class="title">get</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> val</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">set</span>(<span class="params">v</span>)</span> {</span><br><span class="line"> val = v</span><br><span class="line"> update()</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">update</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(obj.foo)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> obj = {}</span><br><span class="line">defineReactive(obj, <span class="string">'foo'</span>, <span class="string">'foo'</span>)</span><br><span class="line">obj.foo = <span class="string">'foooooooo'</span></span><br></pre></td></tr></table></figure><h4 id="Vue3-的方式"><a href="#Vue3-的方式" class="headerlink" title="Vue3 的方式"></a>Vue3 的方式</h4><p>Proxy</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代理整个对象,从⽽侦测数据变化</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">defineReactive</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, {</span><br><span class="line"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> target[key]</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span><br><span class="line"> target[key] = val</span><br><span class="line"> update()</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">update</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(obj.foo)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> obj = {}</span><br><span class="line"><span class="keyword">const</span> observed = defineReactive(obj)</span><br><span class="line">observed.foo = <span class="string">'foooooooo'</span></span><br></pre></td></tr></table></figure><h4 id="Vue2-vs-Vue3"><a href="#Vue2-vs-Vue3" class="headerlink" title="Vue2 vs Vue3"></a>Vue2 vs Vue3</h4><ol><li>vue2 中需要递归遍历对象所有 key,速度慢</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1.对象响应化:遍历每个key,定义getter、setter</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">observe</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> obj !== <span class="string">'object'</span> || obj == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> keys = <span class="built_in">Object</span>.keys(obj)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < keys.length; i++) {</span><br><span class="line"> <span class="keyword">const</span> key = keys[i]</span><br><span class="line"> defineReactive(obj, key, obj[key])</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">defineReactive</span>(<span class="params">obj, key, val</span>) </span>{</span><br><span class="line"> observe(val)</span><br><span class="line"></span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(obj, key, {</span><br><span class="line"> <span class="function"><span class="title">get</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> val</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">set</span>(<span class="params">newVal</span>)</span> {</span><br><span class="line"> <span class="keyword">if</span> (newVal !== val) {</span><br><span class="line"> observe(newVal)</span><br><span class="line"> val = newVal</span><br><span class="line"> dep.notify()</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><ol start="2"><li>数组响应式需要额外实现</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="comment">// 数组响应化:覆盖数组原型⽅法,额外增加通知逻辑</span></span><br><span class="line"><span class="keyword">const</span> originalProto = <span class="built_in">Array</span>.prototype</span><br><span class="line"><span class="keyword">const</span> arrayProto = <span class="built_in">Object</span>.create(originalProto)</span><br><span class="line">;[<span class="string">'push'</span>, <span class="string">'pop'</span>, <span class="string">'shift'</span>, <span class="string">'unshift'</span>, <span class="string">'splice'</span>, <span class="string">'reverse'</span>, <span class="string">'sort'</span>].forEach(</span><br><span class="line"> (method) => {</span><br><span class="line"> arrayProto[method] = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> originalProto[method].apply(<span class="built_in">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line"> dep.notify()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">)</span><br></pre></td></tr></table></figure><ol start="3"><li>新增或删除属性⽆法监听,需要使⽤特殊 api</li></ol><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">Vue.set(obj, <span class="string">'foo'</span>, <span class="string">'bar'</span>)</span><br><span class="line">Vue.delete(obj, <span class="string">'foo'</span>)</span><br></pre></td></tr></table></figure><ol start="4"><li>不⽀持 Map、Set、Class 等数据结构</li></ol><h5 id="vue3-实现"><a href="#vue3-实现" class="headerlink" title="vue3 实现"></a>vue3 实现</h5><p>vue3 中利⽤ es6 proxy 实现数据响应式,很好的解决了以上问题。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><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="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> obj !== <span class="string">'object'</span> && obj != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> obj</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// Proxy相当于在对象外层加拦截</span></span><br><span class="line"> <span class="comment">// http://es6.ruanyifeng.com/#docs/proxy</span></span><br><span class="line"> <span class="keyword">const</span> observed = <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, {</span><br><span class="line"> <span class="function"><span class="title">get</span>(<span class="params">target, key, receiver</span>)</span> {</span><br><span class="line"> <span class="comment">// Reflect⽤于执⾏对象默认操作,更规范、更友好</span></span><br><span class="line"> <span class="comment">// Proxy和Object的⽅法Reflect都有对应</span></span><br><span class="line"> <span class="comment">// http://es6.ruanyifeng.com/#docs/reflect</span></span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.get(target, key, receiver)</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`获取<span class="subst">${key}</span>:<span class="subst">${res}</span>`</span>)</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">set</span>(<span class="params">target, key, value, receiver</span>)</span> {</span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.set(target, key, value, receiver)</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`设置<span class="subst">${key}</span>:<span class="subst">${value}</span>`</span>)</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.deleteProperty(target, key)</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`删除<span class="subst">${key}</span>:<span class="subst">${res}</span>`</span>)</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> observed</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 测试</span></span><br><span class="line"><span class="keyword">const</span> state = reactive({ <span class="attr">foo</span>: <span class="string">'foo'</span> })</span><br><span class="line"><span class="comment">// 获取</span></span><br><span class="line">state.foo <span class="comment">// ok</span></span><br><span class="line"><span class="comment">// 设置已存在属性</span></span><br><span class="line">state.foo = <span class="string">'fooooooo'</span> <span class="comment">// ok</span></span><br><span class="line"><span class="comment">// 设置不存在属性</span></span><br><span class="line">state.dong = <span class="string">'dong'</span> <span class="comment">// ok</span></span><br><span class="line"><span class="comment">// 删除属性</span></span><br><span class="line"><span class="keyword">delete</span> state.dong <span class="comment">// ok</span></span><br></pre></td></tr></table></figure><p>so,当面试官问你。vue2vue3 有什么变化?可以这样回答:</p><p>在数据响应式这块革命性的更新</p><ol><li>初始化速度很快,不需要在一开始就递归遍历所有 key</li><li>维护依赖关系变得更加简单,资源占用会少</li><li>不需要对数组有额外的实现,不需要产生 vue.set 和 vue.delete 这样额外的 api</li><li>对数据类型的支持更广了</li><li>缺点:兼容性很差,连 IE11 都不兼容了,想要兼容就用 vue2.7,vue3 永不支持。vue2.7 是自带 compositionApi 的 Vue2</li></ol><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><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="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, {</span><br><span class="line"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'get'</span>, key)</span><br><span class="line"> <span class="keyword">return</span> target[key]</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'set'</span>, key)</span><br><span class="line"> target[key] = val</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'delete prop'</span>, key)</span><br><span class="line"> <span class="keyword">delete</span> target[key]</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">const</span> state = reactive({</span><br><span class="line"> foo: <span class="string">'foo'</span>,</span><br><span class="line">})</span><br><span class="line">state.foo</span><br><span class="line">state.foo = <span class="string">'fooooo'</span></span><br><span class="line"><span class="keyword">delete</span> state.foo</span><br></pre></td></tr></table></figure><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><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, {</span><br><span class="line"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'get'</span>, key)</span><br><span class="line"> <span class="comment">// 1. 更加健壮,在此期间有什么异常,可以更好的捕获</span></span><br><span class="line"> <span class="comment">// 2. 一定会返回一个结果,在这里return出去</span></span><br><span class="line"> <span class="comment">// 既健壮又明确</span></span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.get(target, key)</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'set'</span>, key)</span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.set(target, key, val)</span><br><span class="line"> <span class="comment">// 这里有个布尔值可以返回出去了,知道最终操作的结果了</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'delete prop'</span>, key)</span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.deleteProperty(target, key)</span><br><span class="line"> <span class="keyword">return</span> res</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">const</span> state = reactive({</span><br><span class="line"> foo: <span class="string">'foo'</span>,</span><br><span class="line">})</span><br><span class="line">state.foo</span><br><span class="line">state.foo = <span class="string">'fooooo'</span></span><br><span class="line"><span class="keyword">delete</span> state.foo</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> isObject = <span class="function">(<span class="params">v</span>) =></span> <span class="keyword">typeof</span> v === <span class="string">'object'</span> && v !== <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, {</span><br><span class="line"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'get'</span>, key)</span><br><span class="line"> <span class="comment">// 1. 更加健壮,在此期间有什么异常,可以更好的捕获</span></span><br><span class="line"> <span class="comment">// 2. 一定会返回一个结果,在这里return出去</span></span><br><span class="line"> <span class="comment">// 既健壮又明确</span></span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.get(target, key)</span><br><span class="line"> <span class="comment">// 懒处理</span></span><br><span class="line"> <span class="keyword">return</span> isObject(res) ? reactive(res) : res</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'set'</span>, key)</span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.set(target, key, val)</span><br><span class="line"> <span class="comment">// 这里有个布尔值可以返回出去了,知道最终操作的结果了</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'delete prop'</span>, key)</span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.deleteProperty(target, key)</span><br><span class="line"> <span class="keyword">return</span> res</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">const</span> state = reactive({</span><br><span class="line"> foo: <span class="string">'foo'</span>,</span><br><span class="line"> bar: {</span><br><span class="line"> baz: <span class="number">1</span>,</span><br><span class="line"> },</span><br><span class="line">})</span><br><span class="line">state.foo</span><br><span class="line">state.foo = <span class="string">'fooooo'</span></span><br><span class="line"><span class="keyword">delete</span> state.foo</span><br><span class="line">state.bar.baz</span><br></pre></td></tr></table></figure><p>改造一下<a href="https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">上文</a>中的例子</p><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></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">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</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><br><span class="line"><span class="javascript"> <span class="keyword">const</span> isObject = <span class="function">(<span class="params">v</span>) =></span> <span class="keyword">typeof</span> v === <span class="string">'object'</span> && v !== <span class="literal">null</span></span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'get'</span>, key)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 1. 更加健壮,在此期间有什么异常,可以更好的捕获</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 2. 一定会返回一个结果,在这里return出去</span></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">const</span> res = <span class="built_in">Reflect</span>.get(target, key)</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">return</span> isObject(res) ? reactive(res) : res</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'set'</span>, key)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.set(target, key, val)</span></span><br><span class="line"> app.update()</span><br><span class="line"><span class="javascript"> <span class="comment">// 这里有个布尔值可以返回出去了,知道最终操作的结果了</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> res</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'delete prop'</span>, key)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.deleteProperty(target, key)</span></span><br><span class="line"> app.update()</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> res</span></span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="comment">// 基本结构</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> Vue = {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createApp</span>(<span class="params">options</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> renderer = Vue.createRenderer({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">querySelector</span>(<span class="params">selector</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> <span class="built_in">document</span>.querySelector(selector)</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">insert</span>(<span class="params">child, parent, anchor</span>)</span> {</span></span><br><span class="line"><span class="javascript"> parent.insertBefore(child, anchor || <span class="literal">null</span>)</span></span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> renderer.createApp(options)</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createRenderer</span>(<span class="params">{ querySelector, insert }</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 平台特有的操作</span></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">return</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createApp</span>(<span class="params">options</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mount</span>(<span class="params">selector</span>)</span> {</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">const</span> parent = querySelector(selector)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 获取渲染函数,编译结果</span></span></span><br><span class="line"> if (!options.render) {</span><br><span class="line"><span class="javascript"> options.render = <span class="built_in">this</span>.compile(parent.innerHTML)</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (options.setup) {</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.setupState = options.setup()</span></span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.data = options.data()</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(<span class="built_in">this</span>, {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 代理目标(是在data还是setup里)和访问的key</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// target指的是app</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span></span><br><span class="line"> if (key in target.setupState) {</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> target.setupState[key]</span></span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> target.data[key]</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span></span><br><span class="line"> if (key in target.setupState) {</span><br><span class="line"> target.setupState[key] = val</span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"> target.data[key] = val</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="javascript"> <span class="comment">// 渲染dom,追加到宿主元素</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.update = <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">const</span> el = options.render.call(<span class="built_in">this</span>.proxy)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// Vue2中是先追加再删除原来的,Vue3直接清空再追加</span></span></span><br><span class="line"><span class="javascript"> parent.innerHTML = <span class="string">''</span></span></span><br><span class="line"> insert(el, parent)</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.update() <span class="comment">// init</span></span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">compile</span>(<span class="params">template</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 返回render</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">render</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 描述视图</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 跳过对template的解析,这里直接写死</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> h3 = <span class="built_in">document</span>.createElement(<span class="string">'h3'</span>)</span></span><br><span class="line"><span class="javascript"> h3.textContent = <span class="built_in">this</span>.title</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> h3</span></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><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp } = Vue</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = createApp({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'hello, vue3!'</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">setup</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> state = reactive({</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'vue3, hello!'</span>,</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> state.title = <span class="string">'hello, hello, vue3'</span></span></span><br><span class="line"> }, 2000)</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> state</span></span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> app.mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>目前的实现,对 app 有强耦合,不通用</p><p>现在需要创建响应式数据和副作用函数之间依赖关系</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/1.png" alt="依赖收集实现原理"></p><p>effect 的作用是把 cb 临时储存在变量 effectStack 里,会立刻调一次 cb,调完之后由于它内部有副作用,所以 getter 会触发。我们希望在 getter 的内部有一个依赖收集函数叫 track,track 的作用是把当前传入的响应式对象的 key 和 cb 建立依赖关系。这关系通过创建一个 weakMap 建立,这个 weakMap 可以保存 target 的 key 和 cb 之间的关系。为什么要是一个 weakMap 呢,因为这个 target 是一个对象,它的 key 用 weakMap 来表示是最合适的。它的 value 又是一个 Map。一个依赖数据的副作用可能不止一个,所以 cb 这里是一个 Set.<br>track 就是订阅,触发之后就发布,触发更新</p><blockquote><p>WeakMap: 相比于 Map,区别主要在于垃圾回收机制。在 Map 中,当 key = null 时,不会被回收,需要 map.delete(key)才会被回收,而 WeakMap,当 key = null 时,就会被回收。另外的区别就是 WeakMap 只有 get,set,has,delete 方法,没有 keys,values,entries,也没有 size,clear。可以参考这篇<a href="https://segmentfault.com/a/1190000015774465">文章</a><br></p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> isObject = <span class="function">(<span class="params">v</span>) =></span> <span class="keyword">typeof</span> v === <span class="string">'object'</span> && v !== <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reactive</span>(<span class="params">obj</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(obj, {</span><br><span class="line"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'get'</span>, key)</span><br><span class="line"> <span class="comment">// 1. 更加健壮,在此期间有什么异常,可以更好的捕获</span></span><br><span class="line"> <span class="comment">// 2. 一定会返回一个结果,在这里return出去</span></span><br><span class="line"> <span class="comment">// 既健壮又明确</span></span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.get(target, key)</span><br><span class="line"> <span class="comment">// 依赖收集</span></span><br><span class="line"> track(target, key)</span><br><span class="line"> <span class="comment">// 懒处理</span></span><br><span class="line"> <span class="keyword">return</span> isObject(res) ? reactive(res) : res</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'set'</span>, key)</span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.set(target, key, val)</span><br><span class="line"> trigger(target, key)</span><br><span class="line"> <span class="comment">// 这里有个布尔值可以返回出去了,知道最终操作的结果了</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"> },</span><br><span class="line"> <span class="function"><span class="title">deleteProperty</span>(<span class="params">target, key</span>)</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'delete prop'</span>, key)</span><br><span class="line"> <span class="keyword">const</span> res = <span class="built_in">Reflect</span>.deleteProperty(target, key)</span><br><span class="line"> trigger(target, key)</span><br><span class="line"> <span class="keyword">return</span> res</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="comment">// 创建响应式数据和副作用函数之间依赖关系</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 临时保存响应式函数(传入的fn)</span></span><br><span class="line"><span class="keyword">const</span> effectStack = []</span><br><span class="line"></span><br><span class="line"><span class="comment">// effect: 添加副作用函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">effect</span>(<span class="params">fn</span>) </span>{</span><br><span class="line"> <span class="comment">// 把传入的fn封装一下</span></span><br><span class="line"> <span class="keyword">const</span> eff = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 入栈</span></span><br><span class="line"> effectStack.push(eff)</span><br><span class="line"> <span class="comment">// 执行</span></span><br><span class="line"> fn()</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> <span class="comment">// 出栈</span></span><br><span class="line"> effectStack.pop()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 立即执行,激活依赖收集的过程</span></span><br><span class="line"> eff()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> eff</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 存储依赖关系的map</span></span><br><span class="line"><span class="keyword">const</span> targetMap = <span class="keyword">new</span> <span class="built_in">WeakMap</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 依赖收集</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">track</span>(<span class="params">target, key</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> eff = effectStack[effectStack.length - <span class="number">1</span>]</span><br><span class="line"> <span class="keyword">if</span> (eff) {</span><br><span class="line"> <span class="comment">// 1. 先获取target对应的map</span></span><br><span class="line"> <span class="keyword">let</span> depMap = targetMap.get(target)</span><br><span class="line"> <span class="keyword">if</span> (!depMap) {</span><br><span class="line"> <span class="comment">// 首次访问不存在,则创建</span></span><br><span class="line"> depMap = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line"> targetMap.set(target, depMap)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 2. 获取key对应的set</span></span><br><span class="line"> <span class="keyword">let</span> deps = depMap.get(key)</span><br><span class="line"> <span class="keyword">if</span> (!deps) {</span><br><span class="line"> <span class="comment">// 首次访问不存在,则创建</span></span><br><span class="line"> deps = <span class="keyword">new</span> <span class="built_in">Set</span>()</span><br><span class="line"> depMap.set(key, deps)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 3. 建立target,key和eff之间的关系</span></span><br><span class="line"> deps.add(eff)</span><br><span class="line"> }</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="function"><span class="keyword">function</span> <span class="title">trigger</span>(<span class="params">target, key</span>) </span>{</span><br><span class="line"> <span class="comment">// 通过target获取map</span></span><br><span class="line"> <span class="keyword">const</span> depMap = targetMap.get(target)</span><br><span class="line"> <span class="keyword">if</span> (depMap) {</span><br><span class="line"> <span class="comment">// 通过key获取deps</span></span><br><span class="line"> <span class="keyword">const</span> deps = depMap.get(key)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (deps) {</span><br><span class="line"> <span class="comment">// 执行所有副作用</span></span><br><span class="line"> deps.forEach(<span class="function">(<span class="params">dep</span>) =></span> dep())</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">const</span> state = reactive({</span><br><span class="line"> foo: <span class="string">'foo'</span>,</span><br><span class="line"> bar: {</span><br><span class="line"> baz: <span class="number">1</span>,</span><br><span class="line"> },</span><br><span class="line">})</span><br><span class="line"><span class="comment">// state.foo</span></span><br><span class="line"><span class="comment">// state.foo = 'fooooo'</span></span><br><span class="line"><span class="comment">// delete state.foo</span></span><br><span class="line"><span class="comment">// state.bar.baz</span></span><br><span class="line">effect(<span class="function">() =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'effect1'</span>, state.foo)</span><br><span class="line">})</span><br><span class="line">effect(<span class="function">() =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'effect2'</span>, state.foo, state.bar.baz)</span><br><span class="line">})</span><br><span class="line">state.bar.baz = <span class="string">'fooooooo'</span></span><br></pre></td></tr></table></figure><p>与 React 不用,依赖关系是自动建立的,不需要[]指定</p><p>把写完的这个 reactive.js 引入 myvue.html</p><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></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">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</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">script</span> <span class="attr">src</span>=<span class="string">"./reactive.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></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">const</span> Vue = {</span></span><br><span class="line"> ...</span><br><span class="line"> createRenderer({</span><br><span class="line"> querySelector,</span><br><span class="line"> insert</span><br><span class="line"> }) {</span><br><span class="line"><span class="javascript"> <span class="comment">// 平台特有的操作</span></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">return</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createApp</span>(<span class="params">options</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mount</span>(<span class="params">selector</span>)</span> {</span></span><br><span class="line"> ...</span><br><span class="line"><span class="javascript"> <span class="comment">// 渲染dom,追加到宿主元素</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.update = effect(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> el = options.render.call(<span class="built_in">this</span>.proxy)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// Vue2中是先追加再删除原来的,Vue3直接清空再追加</span></span></span><br><span class="line"><span class="javascript"> parent.innerHTML = <span class="string">''</span></span></span><br><span class="line"> insert(el, parent)</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="comment">// this.update() // init</span></span></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><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp } = Vue</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = createApp({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'hello, vue3!'</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">setup</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> state = reactive({</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'vue3, hello!'</span>,</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> state.title = <span class="string">'hello, hello, vue3'</span></span></span><br><span class="line"> }, 2000)</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> state</span></span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> app.mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>使用 effect 改造下 update 方法,就完成了</p><p>目前不是精准更新,现在是对整个组件的全部更新</p><h3 id="看源码"><a href="#看源码" class="headerlink" title="看源码"></a>看源码</h3><p><code>packages/runtime-core/src/renderer.ts</code></p><p>程序首次 mount 的时候,会调用 render 方法</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/2.png" alt="render"></p><p>render 会调用 patch</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/3.png" alt="patch"></p><p>patch 第一次会走 processComponent</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/4.png" alt="processComponent"></p><p>第一次 n1 为 null,会走 mountComponent</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/5.png" alt="mountComponent"></p><p>首次挂载:1.根组件实例化 2.初始化更组件 3.安装 render 函数副作用 setupRenderEffect</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/6.png" alt="setupRenderEffect"></p><p>setupRenderEffect 中实际就只有一行代码<code>instance.update = effect(function componentEffect() {})</code></p><p>这里和我们上面写的其实是一样的,我们是全量更新,这里做了虚拟 dom</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/7.png" alt="手写版"></p><p>源码中,会判断 instance.isMounted,不是首次的话会进行 diff,手写版中也省略了</p><p>再看回 mountComponent 中的 setupComponent</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/8.png" alt="setupComponent"></p><p>先是判断<code>isStatefulComponent</code>,是否是状态组件(有属性有 data),就会执行 setupStatefulComponent</p><p>setupStatefulComponent 会做一个数据的代理,这个和手写版中下面这段是一样的</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/9.png" alt="proxy"></p><p>接着往下,判断 setup 是否存在,存在就执行 setup。无论 setup 是否存在,都会执行 finishComponentSetup,在 handleSetupResult 中同样也调用了</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/10.png" alt="finishComponentSetup"></p><p>其中 options 无论如何都会处理(applyOptions)</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/11.png" alt="applyOptions"></p><p>这里 data 被重命名为 dataOptions,找到他</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/12.png" alt="dataOptions"></p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/13.png" alt="resolveData"></p><p>可以发现,不管是 setup 中使用<code>reactive()</code>主动的响应式,还是使用<code>data(){return{}}</code>的被动的响应式,最后都是 reactive 在对对象在做代理</p><h4 id="reactivity"><a href="#reactivity" class="headerlink" title="reactivity"></a>reactivity</h4><p><code>packages/reactivity</code>Vue3 中把 reactive 被单独抽离出去成了 reactivity 这个模块<br>reactivity 可以单独发布到 npm 上,让人可以单独安装。</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/14.png" alt="reactive"></p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/15.png" alt="createReactiveObject"></p><p>最终返回 proxy</p><p>数据的拦截结束,下面是副作用的添加</p><p><code>packages/reactivity/src/effect.ts</code></p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/16.png" alt="effect"></p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/17.png" alt="createReactiveEffect"></p><p>这里和手写版的 effect 是一样了</p><p>再来看下 track,和手写版基本一致</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/18.png" alt="track"></p><p>trigger 的话有些区别,为了防止数组的一些抖动问题,做了些特殊判断</p><p>不过目的依然是拿到依赖,去执行它们</p><p><img src="/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/19.png" alt="trigger"></p><p>这里的 run 本质上就是执行 effect,除了用户在 options 上设置了 scheduler</p><p><strong>本文完</strong></p>]]></content>
<summary type="html"><p>目录:</p>
<ul>
<li><a href="https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li>
<li><a href="htt</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="手撕源码" scheme="https://yongmaple.com/tags/%E6%89%8B%E6%92%95%E6%BA%90%E7%A0%81/"/>
<category term="Vue3" scheme="https://yongmaple.com/tags/Vue3/"/>
</entry>
<entry>
<title>Vue3源码解析</title>
<link href="https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
<id>https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</id>
<published>2021-05-26T13:55:37.000Z</published>
<updated>2021-08-29T13:38:44.188Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li><li><a href="https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/">Vue2 源码解析二</a></li><li><a href="https://yongmaple.com/2021/05/31/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/">Vue2 源码解析三</a></li></ul><p>本文项目地址:<a href="https://github.com/YongMaple/vue-next">https://github.com/YongMaple/vue-next</a></p><h3 id="Vue3-初体验"><a href="#Vue3-初体验" class="headerlink" title="Vue3 初体验"></a>Vue3 初体验</h3><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></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">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{state.title}}<span class="tag"></<span class="name">h3</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">comp</span>></span><span class="tag"></<span class="name">comp</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 class="attr">src</span>=<span class="string">"http://unpkg.com/vue@next"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp, reactive } = Vue</span></span><br><span class="line"><span class="javascript"> <span class="comment">// new Vue</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 函数式:类型支持会好(ts),摇树优化(打包体积减小)</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 消灭静态方法(Vue.component...),变成实例方法 例如component如果没用,就不会被打包进去</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 以前在Vue.prototype里面做配置,会污染全局构造函数,现在可以创建多个实例不会互相污染,实例之间相互独立</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// const app2 = createApp({})</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 全局配置可以通过app.config.globalProperties.XXX来实现</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = createApp({</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 统一api,一致性</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'hello, vue3!'</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">setup</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> state = reactive({</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'vue3, hello'</span>,</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"> state,</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="comment">// 全局组件</span></span></span><br><span class="line"><span class="javascript"> app.component(<span class="string">'comp'</span>, {</span></span><br><span class="line"><span class="handlebars"><span class="xml"> template: '<span class="tag"><<span class="name">div</span>></span>comp<span class="tag"></<span class="name">div</span>></span>',</span></span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="comment">// api简化、一致性更强</span></span></span><br><span class="line"><span class="javascript"> app.mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><ul><li>动机</li></ul><ol><li>类型⽀持更好</li><li>利于 tree-shaking</li><li>API 简化、⼀致性:render 函数,sync 修饰符,指令定义等</li><li>复⽤性:composition api</li><li>性能优化:响应式、编译优化</li><li>扩展性:⾃定义渲染器</li></ol><h3 id="手写-Vue3"><a href="#手写-Vue3" class="headerlink" title="手写 Vue3"></a>手写 Vue3</h3><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></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">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</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><br><span class="line"><span class="javascript"> <span class="comment">// 基本结构</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> Vue = {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createApp</span>(<span class="params">options</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 返回app实例</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mount</span>(<span class="params">selector</span>)</span> {</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">const</span> parent = <span class="built_in">document</span>.querySelector(selector)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 获取渲染函数,编译结果</span></span></span><br><span class="line"> if (!options.render) {</span><br><span class="line"><span class="javascript"> options.render = <span class="built_in">this</span>.compile(parent.innerHTML)</span></span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="comment">// 渲染dom,追加到宿主元素</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(options.render)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> el = options.render.call(options.data())</span></span><br><span class="line"><span class="javascript"> <span class="comment">// Vue2中是先追加再删除原来的,Vue3直接清空再追加</span></span></span><br><span class="line"><span class="javascript"> parent.innerHTML = <span class="string">''</span></span></span><br><span class="line"> parent.appendChild(el)</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">compile</span>(<span class="params">template</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 返回render</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">render</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 描述视图</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 跳过对template的解析,这里直接写死</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> h3 = <span class="built_in">document</span>.createElement(<span class="string">'h3'</span>)</span></span><br><span class="line"><span class="javascript"> h3.textContent = <span class="built_in">this</span>.title</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> h3</span></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="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp } = Vue</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = createApp({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'hello, vue3!'</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> app.mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><h4 id="兼容-Composition-API"><a href="#兼容-Composition-API" class="headerlink" title="兼容 Composition API"></a>兼容 Composition API</h4><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></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">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</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><br><span class="line"><span class="javascript"> <span class="comment">// 基本结构</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> Vue = {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createApp</span>(<span class="params">options</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 返回app实例</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mount</span>(<span class="params">selector</span>)</span> {</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">const</span> parent = <span class="built_in">document</span>.querySelector(selector)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 获取渲染函数,编译结果</span></span></span><br><span class="line"> if (!options.render) {</span><br><span class="line"><span class="javascript"> options.render = <span class="built_in">this</span>.compile(parent.innerHTML)</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (options.setup) {</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.setupState = options.setup()</span></span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.data = options.data()</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(<span class="built_in">this</span>, {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 代理目标(是在data还是setup里)和访问的key</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// target指的是app</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span></span><br><span class="line"> if (key in target.setupState) {</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> target.setupState[key]</span></span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> target.data[key]</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span></span><br><span class="line"> if (key in target.setupState) {</span><br><span class="line"> target.setupState[key] = val</span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"> target.data[key] = val</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="javascript"> <span class="comment">// 渲染dom,追加到宿主元素</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> el = options.render.call(<span class="built_in">this</span>.proxy)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// Vue2中是先追加再删除原来的,Vue3直接清空再追加</span></span></span><br><span class="line"><span class="javascript"> parent.innerHTML = <span class="string">''</span></span></span><br><span class="line"> parent.appendChild(el)</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">compile</span>(<span class="params">template</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 返回render</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">render</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 描述视图</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 跳过对template的解析,这里直接写死</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> h3 = <span class="built_in">document</span>.createElement(<span class="string">'h3'</span>)</span></span><br><span class="line"><span class="javascript"> h3.textContent = <span class="built_in">this</span>.title</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> h3</span></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="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp } = Vue</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = createApp({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'hello, vue3!'</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">setup</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'vue3, hello!'</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> app.mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><h4 id="提高扩展性,兼容各平台"><a href="#提高扩展性,兼容各平台" class="headerlink" title="提高扩展性,兼容各平台"></a>提高扩展性,兼容各平台</h4><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></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">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</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><br><span class="line"><span class="javascript"> <span class="comment">// 基本结构</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> Vue = {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createApp</span>(<span class="params">options</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> renderer = Vue.createRenderer({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">querySelector</span>(<span class="params">selector</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> <span class="built_in">document</span>.querySelector(selector)</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">insert</span>(<span class="params">child, parent, anchor</span>)</span> {</span></span><br><span class="line"><span class="javascript"> parent.insertBefore(child, anchor || <span class="literal">null</span>)</span></span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> renderer.createApp(options)</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createRenderer</span>(<span class="params">{ querySelector, insert }</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 平台特有的操作</span></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">return</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createApp</span>(<span class="params">options</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mount</span>(<span class="params">selector</span>)</span> {</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">const</span> parent = querySelector(selector)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 获取渲染函数,编译结果</span></span></span><br><span class="line"> if (!options.render) {</span><br><span class="line"><span class="javascript"> options.render = <span class="built_in">this</span>.compile(parent.innerHTML)</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (options.setup) {</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.setupState = options.setup()</span></span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.data = options.data()</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(<span class="built_in">this</span>, {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 代理目标(是在data还是setup里)和访问的key</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// target指的是app</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">get</span>(<span class="params">target, key</span>)</span> {</span></span><br><span class="line"> if (key in target.setupState) {</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> target.setupState[key]</span></span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> target.data[key]</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">set</span>(<span class="params">target, key, val</span>)</span> {</span></span><br><span class="line"> if (key in target.setupState) {</span><br><span class="line"> target.setupState[key] = val</span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"> target.data[key] = val</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="javascript"> <span class="comment">// 渲染dom,追加到宿主元素</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> el = options.render.call(<span class="built_in">this</span>.proxy)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// Vue2中是先追加再删除原来的,Vue3直接清空再追加</span></span></span><br><span class="line"><span class="javascript"> parent.innerHTML = <span class="string">''</span></span></span><br><span class="line"> insert(el, parent)</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">compile</span>(<span class="params">template</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 返回render</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">render</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 描述视图</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 跳过对template的解析,这里直接写死</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> h3 = <span class="built_in">document</span>.createElement(<span class="string">'h3'</span>)</span></span><br><span class="line"><span class="javascript"> h3.textContent = <span class="built_in">this</span>.title</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> h3</span></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><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createApp } = Vue</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = createApp({</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'hello, vue3!'</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">setup</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'vue3, hello!'</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> app.mount(<span class="string">'#app'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><h3 id="源码学习"><a href="#源码学习" class="headerlink" title="源码学习"></a>源码学习</h3><ul><li>版本:3.1.0-beta.4</li><li>添加 sourcemap,<code>"dev": "node scripts/dev.js --sourcemap",</code></li><li>安装依赖:<code>yarn --ignore-scripts</code></li><li>编译:<code>yarn dev</code>,编译完会在<code>packages/vue/dist</code>中</li><li>调试范例代码:<code>yarn serve</code></li></ul><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/1.PNG" alt="初始化流程"></p><p>源码路径:<code>packages/runtime-dom/src/index.ts</code></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/2.png" alt="createApp"></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/3.png" alt="ensureRenderer"></p><p>源码路径:<code>packages/runtime-core/src/renderer.ts</code> <em>核心代码几乎都在这</em></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/4.png" alt="createRenderer"></p><p><code>baseCreateRenderer</code>函数有 2000 行代码……</p><p>最后和上面手写的一样,返回了渲染器,不过这更加完善</p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/5.png" alt="baseCreateRenderer"></p><p>源码路径:<code>packages/runtime-core/src/apiCreateApp.ts</code></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/6.png" alt="createAppAPI"></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/7.png" alt="render"></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/8.png" alt="patch"></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/9.png" alt="processComponent"></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/10.png" alt="mountComponent"></p><p><img src="/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/11.png" alt="setupRenderEffect"></p><h3 id="自定义渲染器"><a href="#自定义渲染器" class="headerlink" title="自定义渲染器"></a>自定义渲染器</h3><p>代码位置:<code>packages/vue/examples/canvasApp.html</code></p><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></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">"demo"</span>></span><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 class="attr">src</span>=<span class="string">"../dist/vue.global.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { createRenderer } = Vue</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">const</span> nodeOps = {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// querySelector() {},</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">createElement</span>(<span class="params">tag</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 由于是在canvas中绘图,所以不需要创建dom,仅保存数据即可</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"> tag,</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">patchProp</span>(<span class="params">el, key, prev, next</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// el 就是前面createElement返回的对象</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 只需要保存数据即可</span></span></span><br><span class="line"> el[key] = next</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">insert</span>(<span class="params">child, parent, anchor</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 由于是在canvas中绘图,所以不需要插入dom</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 只需要绘制即可</span></span></span><br><span class="line"> if (parent.nodeType === 1) {</span><br><span class="line"><span class="javascript"> <span class="comment">// parent是canvas</span></span></span><br><span class="line"> draw(child)</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="javascript"> <span class="keyword">const</span> renderer = createRenderer(nodeOps)</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> canvas, ctx</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> draw = <span class="function">(<span class="params">el, noClear</span>) =></span> {</span></span><br><span class="line"> if (!noClear) {</span><br><span class="line"> ctx.clearRect(0, 0, canvas.width, canvas.height)</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (el.tag === <span class="string">'bar-chart'</span>) {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { data } = el</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> barWidth = canvas.width / <span class="number">10</span>,</span></span><br><span class="line"> gap = 20,</span><br><span class="line"> paddingLeft = (data.length * barWidth + (data.length - 1) * gap) / 2,</span><br><span class="line"> paddingBottom = 10</span><br><span class="line"><span class="javascript"> <span class="comment">// x轴</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 柱状图</span></span></span><br><span class="line"><span class="javascript"> data.forEach(<span class="function">(<span class="params">{ title, count, color }, index</span>) =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> x = paddingLeft + index * (barWidth + gap)</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> y = canvas.height - paddingBottom - count</span></span><br><span class="line"> ctx.fillStyle = color</span><br><span class="line"> ctx.fillRect(x, y, barWidth, count)</span><br><span class="line"><span class="javascript"> <span class="comment">// text</span></span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> el.childs && el.childs.forEach(<span class="function">(<span class="params">child</span>) =></span> draw(child, <span class="literal">true</span>))</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">createCavansApp</span>(<span class="params">options</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = renderer.createApp(options)</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> mount = app.mount</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> app.mount = <span class="function"><span class="keyword">function</span> (<span class="params">sel</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 额外创建一个宿主canvas</span></span></span><br><span class="line"><span class="javascript"> canvas = <span class="built_in">document</span>.createElement(<span class="string">'canvas'</span>)</span></span><br><span class="line"><span class="javascript"> canvas.width = <span class="built_in">window</span>.innerWidth</span></span><br><span class="line"><span class="javascript"> canvas.height = <span class="built_in">window</span>.innerHeight</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.querySelector(sel).appendChild(canvas)</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> ctx = canvas.getContext(<span class="string">'2d'</span>)</span></span><br><span class="line"></span><br><span class="line"> mount(canvas)</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> app</span></span><br><span class="line"> }</span><br><span class="line"> createCavansApp({</span><br><span class="line"><span class="handlebars"><span class="xml"> template: '<span class="tag"><<span class="name">bar-chart</span> <span class="attr">:data</span>=<span class="string">"chartData"</span>></span><span class="tag"></<span class="name">bar-chart</span>></span>',</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"> chartData: [</span><br><span class="line"> {</span><br><span class="line"><span class="javascript"> title: <span class="string">'青铜'</span>,</span></span><br><span class="line"> count: 200,</span><br><span class="line"><span class="javascript"> color: <span class="string">'brown'</span>,</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"><span class="javascript"> title: <span class="string">'钻石'</span>,</span></span><br><span class="line"> count: 300,</span><br><span class="line"><span class="javascript"> color: <span class="string">'skyblue'</span>,</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"><span class="javascript"> title: <span class="string">'星耀'</span>,</span></span><br><span class="line"> count: 100,</span><br><span class="line"><span class="javascript"> color: <span class="string">'purple'</span>,</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"><span class="javascript"> title: <span class="string">'王者'</span>,</span></span><br><span class="line"> count: 50,</span><br><span class="line"><span class="javascript"> color: <span class="string">'gold'</span>,</span></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="javascript"> }).mount(<span class="string">'#demo'</span>)</span></span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p><strong>本文完</strong></p>]]></content>
<summary type="html"><p>目录:</p>
<ul>
<li><a href="https://yongmaple.com/2021/05/26/Vue3%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li>
<li><a href="htt</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="手撕源码" scheme="https://yongmaple.com/tags/%E6%89%8B%E6%92%95%E6%BA%90%E7%A0%81/"/>
<category term="Vue3" scheme="https://yongmaple.com/tags/Vue3/"/>
</entry>
<entry>
<title>indexedDB中遇到的问题</title>
<link href="https://yongmaple.com/2021/05/24/indexedDB%E4%B8%AD%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98/"/>
<id>https://yongmaple.com/2021/05/24/indexedDB%E4%B8%AD%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98/</id>
<published>2021-05-24T17:20:25.000Z</published>
<updated>2021-08-29T13:38:44.224Z</updated>
<content type="html"><![CDATA[<ol><li> index 查找时不能使用布尔值</li></ol><p>原本<code>isReverse</code>传的是布尔,会报错<br><code>DataError: Failed to execute 'get' on 'IDBIndex': The parameter is not a valid key.</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><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> indexedDBRead = <span class="function">(<span class="params">id, isReverse</span>) =></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 class="keyword">const</span> dbRequest = <span class="built_in">window</span>.indexedDB.open(<span class="string">'blackbox'</span>, <span class="number">1</span>)</span><br><span class="line"> dbRequest.onsuccess = <span class="function">(<span class="params">e</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> transaction = e.target.result.transaction(</span><br><span class="line"> [<span class="string">'tempContacts'</span>],</span><br><span class="line"> <span class="string">'readwrite'</span></span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">const</span> objectStore = transaction.objectStore(<span class="string">'tempContacts'</span>)</span><br><span class="line"> <span class="keyword">const</span> index = objectStore.index(<span class="string">'myIndex'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> request = index.get([id, <span class="literal">false</span>])</span><br><span class="line"></span><br><span class="line"> request.onerror = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>{</span><br><span class="line"> reject()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> request.onsuccess = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>{</span><br><span class="line"> resolve(request.result)</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><ol start="2"><li>所有的事务,要在 onsuccess 中写,如下</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><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="keyword">const</span> indexedDBAdd = <span class="function">(<span class="params">obj</span>) =></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 class="keyword">const</span> dbRequest = <span class="built_in">window</span>.indexedDB.open(<span class="string">'blackbox'</span>, <span class="number">1</span>)</span><br><span class="line"> dbRequest.onsuccess = <span class="function">(<span class="params">e</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> transaction = dbRequest.result.transaction(</span><br><span class="line"> [<span class="string">'tempContacts'</span>],</span><br><span class="line"> <span class="string">'readwrite'</span></span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">const</span> objectStore = transaction.objectStore(<span class="string">'tempContacts'</span>)</span><br><span class="line"> <span class="keyword">const</span> request = objectStore.add(obj)</span><br><span class="line"> request.onsuccess = <span class="function">(<span class="params">event</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'数据写入成功'</span>)</span><br><span class="line"> resolve(<span class="literal">true</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> request.onerror = <span class="function">(<span class="params">event</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'数据写入失败'</span>)</span><br><span class="line"> reject()</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">const</span> indexedDBDel = <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">const</span> dbRequest = <span class="built_in">window</span>.indexedDB.open(<span class="string">'blackbox'</span>, <span class="number">1</span>)</span><br><span class="line"> dbRequest.onsuccess = <span class="function">(<span class="params">e</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> transaction = dbRequest.result.transaction(</span><br><span class="line"> [<span class="string">'tempContacts'</span>],</span><br><span class="line"> <span class="string">'readwrite'</span></span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">const</span> objectStore = transaction.objectStore(<span class="string">'tempContacts'</span>)</span><br><span class="line"> objectStore.clear()</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">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><ol>
<li> index 查找时不能使用布尔值</li>
</ol>
<p>原本<code>isReverse</code>传的是布尔,会报错<br><code>DataError: Failed to execute &#39;get&#39; on &#39;IDBIn</summary>
<category term="报错" scheme="https://yongmaple.com/categories/%E6%8A%A5%E9%94%99/"/>
<category term="报错" scheme="https://yongmaple.com/tags/%E6%8A%A5%E9%94%99/"/>
<category term="indexedDB" scheme="https://yongmaple.com/tags/indexedDB/"/>
</entry>
<entry>
<title>前端笔试题集合</title>
<link href="https://yongmaple.com/2021/05/21/%E5%89%8D%E7%AB%AF%E7%AC%94%E8%AF%95%E9%A2%98%E9%9B%86%E5%90%88/"/>
<id>https://yongmaple.com/2021/05/21/%E5%89%8D%E7%AB%AF%E7%AC%94%E8%AF%95%E9%A2%98%E9%9B%86%E5%90%88/</id>
<published>2021-05-21T14:53:22.000Z</published>
<updated>2021-08-29T13:38:44.224Z</updated>
<content type="html"><![CDATA[<h4 id="不定期更新,以下都是我遇到过或看到过的真实面试题,答案均为自己做的,如果有不同意见,请留言指正"><a href="#不定期更新,以下都是我遇到过或看到过的真实面试题,答案均为自己做的,如果有不同意见,请留言指正" class="headerlink" title="不定期更新,以下都是我遇到过或看到过的真实面试题,答案均为自己做的,如果有不同意见,请留言指正"></a>不定期更新,以下都是我遇到过或看到过的真实面试题,答案均为自己做的,如果有不同意见,请留言指正</h4><ol><li>用 css 实现布局:左侧区域固定宽度 220px,右侧区域自适应(右侧顶部有 64px 高度的 header)</li></ol><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><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="tag"><<span class="name">style</span>></span></span><br><span class="line"> .wrap {</span><br><span class="line"><span class="css"> <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="css"> <span class="attribute">height</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="css"> <span class="attribute">display</span>: grid;</span></span><br><span class="line"><span class="css"> grid-template-<span class="attribute">columns</span>: <span class="number">220px</span> <span class="number">1</span>fr;</span></span><br><span class="line"><span class="css"> grid-template-rows: <span class="number">64px</span> <span class="number">1</span>fr;</span></span><br><span class="line"> grid-template-areas:</span><br><span class="line"> 'left header'</span><br><span class="line"> 'left main';</span><br><span class="line"> }</span><br><span class="line"> .left {</span><br><span class="line"><span class="css"> grid-area: left;</span></span><br><span class="line"> }</span><br><span class="line"> .header {</span><br><span class="line"><span class="css"> grid-area: header;</span></span><br><span class="line"> }</span><br><span class="line"> .main {</span><br><span class="line"><span class="css"> grid-area: main;</span></span><br><span class="line"> }</span><br><span class="line"><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">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"wrap"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</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">"header"</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">"main"</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">body</span>></span></span><br></pre></td></tr></table></figure><ol start="2"><li>将一个整型数组 A 作为参数传递给一个函数 B,B 函数内部修改传递进来的整型数组的第一个元素值,执行完 B 函数后,数组 A 中的元素值是否发生变化?解释其原因</li></ol><p>会发生变化,因为数组是对象,对象传递时是传递的内存地址(引用传递),实际地址指向是同一个对象</p><ol start="3"><li>创建 10 个 a 标签,点击输出其序号</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="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">const</span> dom = <span class="built_in">document</span>.createElement(<span class="string">'a'</span>)</span><br><span class="line"> dom.innerText = i</span><br><span class="line"> dom.onclick = <span class="function">() =></span> {</span><br><span class="line"> alert(i)</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">document</span>.body.appendChild(dom)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="4"><li>请实现一个函数,可将无限层级的数组进行展平。比如:<code>[1,[2,[3,4]],5]</code>,展平后为<code>[1,2,3,4,5]</code></li></ol><p>最简单的方式,用 flat,默认展开 1 级,Infinity 表示无限层级</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">const</span> original = [<span class="number">1</span>, [<span class="number">2</span>, [<span class="number">3</span>, <span class="number">4</span>]], <span class="number">5</span>]</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">unflod</span>(<span class="params">original</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> original.flat(<span class="literal">Infinity</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><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">const</span> original = [<span class="number">1</span>, [<span class="number">2</span>, [<span class="number">3</span>, <span class="number">4</span>]], <span class="number">5</span>]</span><br><span class="line"><span class="keyword">const</span> result = []</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">unfold</span>(<span class="params">original</span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < original.length; i++) {</span><br><span class="line"> <span class="keyword">const</span> item = original[i]</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(item)) {</span><br><span class="line"> unfold(item)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> result.push(item)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">unfold(original)</span><br><span class="line"><span class="built_in">console</span>.log(result)</span><br></pre></td></tr></table></figure><ol start="5"><li>寻找单数,整数集合中有 2n+1 个数,其中 2n 个数两两相同,寻找剩下对单数,如:<code>[2,3,6,3,6,1,1]</code>,输出:2</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="function"><span class="keyword">function</span> <span class="title">fn</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> result = <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 < arr.length; i++) {</span><br><span class="line"> result ^= arr[i]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(fn([<span class="number">2</span>, <span class="number">3</span>, <span class="number">6</span>, <span class="number">3</span>, <span class="number">6</span>, <span class="number">1</span>, <span class="number">1</span>]))</span><br></pre></td></tr></table></figure><ol start="6"><li><p>请实现 Promise.all 与 Promise.race 方法</p></li><li><p>请实现一个函数,统计数组里字符的出现次数,例如:</p></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="keyword">var</span> ret = analyse(arr)</span><br><span class="line"><span class="comment">// 结果:ret = {</span></span><br><span class="line"><span class="comment">// 1:2,</span></span><br><span class="line"><span class="comment">// 2:1,</span></span><br><span class="line"><span class="comment">// 3:1</span></span><br><span class="line"><span class="comment">// }</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">analyse</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> res = {}</span><br><span class="line"> arr.forEach(<span class="function">(<span class="params">v</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (!res[v]) res[v] = <span class="number">1</span></span><br><span class="line"> <span class="keyword">else</span> res[v] += <span class="number">1</span></span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="8"><li>简单布局:左固定右不固定</li></ol><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><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">"parent"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"a"</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">id</span>=<span class="string">"b"</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">body</span>></span></span><br></pre></td></tr></table></figure><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-id">#parent</span> {</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line">}</span><br><span class="line"><span class="selector-id">#a</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-id">#b</span> {</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="9"><li>请实现一个算法,能够将给定字符串给定的数量循环移动,例如:</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">const</span> str = <span class="string">'abcdef'</span></span><br><span class="line"><span class="keyword">const</span> ret = move(str, <span class="number">2</span>)</span><br><span class="line"><span class="comment">// 结果:ret为efabcd</span></span><br><span class="line"><span class="keyword">const</span> str = <span class="string">'abcdef'</span></span><br><span class="line"><span class="keyword">const</span> ret = move(str, <span class="number">11</span>)</span><br><span class="line"><span class="comment">// 结果:ret为bcdefa</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="function"><span class="keyword">function</span> <span class="title">move</span>(<span class="params">str, num</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> len = str.length</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> str.substr(len - (num % len), num % len) + str.substr(<span class="number">0</span>, len - (num % len))</span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="10"><li>请实现 filter 函数,该函数功能如下:</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ret = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>].filter(<span class="function">(<span class="params">i</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span> % <span class="number">2</span> === <span class="number">0</span></span><br><span class="line">})</span><br><span class="line"><span class="comment">// 结果:ret = [2,4]</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><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.prop\totype.filter = <span class="function"><span class="keyword">function</span>(<span class="params">fn, ctx</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> fn !== <span class="string">'function'</span>) <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>()</span><br><span class="line"> <span class="keyword">let</span> len = <span class="built_in">this</span>.length >>> <span class="number">0</span>,</span><br><span class="line"> res = <span class="keyword">new</span> <span class="built_in">Array</span>(len),</span><br><span class="line"> t = <span class="built_in">this</span>,</span><br><span class="line"> c = <span class="number">0</span>,</span><br><span class="line"> i = -<span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> (ctx === <span class="literal">undefined</span>) {</span><br><span class="line"> <span class="keyword">while</span> (++i !== len) {</span><br><span class="line"> <span class="keyword">if</span> (i <span class="keyword">in</span> <span class="built_in">this</span>) {</span><br><span class="line"> <span class="keyword">if</span> (fn(t[i], i ,t)) {</span><br><span class="line"> res[c++] = t[i]</span><br><span class="line"> }</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="keyword">while</span>(++i !== len) {</span><br><span class="line"> <span class="keyword">if</span> (i <span class="keyword">in</span> <span class="built_in">this</span>) {</span><br><span class="line"> <span class="keyword">if</span> (fn.call(ctx, t[i], i,t)) {</span><br><span class="line"> res[c++] = t[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"> res.length = c</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h4 id="不定期更新,以下都是我遇到过或看到过的真实面试题,答案均为自己做的,如果有不同意见,请留言指正"><a href="#不定期更新,以下都是我遇到过或看到过的真实面试题,答案均为自己做的,如果有不同意见,请留言指正" class="headerlink" title</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="面试" scheme="https://yongmaple.com/tags/%E9%9D%A2%E8%AF%95/"/>
<category term="笔试" scheme="https://yongmaple.com/tags/%E7%AC%94%E8%AF%95/"/>
</entry>
<entry>
<title>Vue项目最佳实践</title>
<link href="https://yongmaple.com/2021/05/20/Vue%E9%A1%B9%E7%9B%AE%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/"/>
<id>https://yongmaple.com/2021/05/20/Vue%E9%A1%B9%E7%9B%AE%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/</id>
<published>2021-05-20T14:27:19.000Z</published>
<updated>2021-08-29T13:38:44.224Z</updated>
<content type="html"><![CDATA[<h3 id="版本"><a href="#版本" class="headerlink" title="版本"></a>版本</h3><p>这里使用的是<code>@vue/cli 4.5.13</code>,简单的新建了一个vue2的项目</p><h3 id><a href="#" class="headerlink" title></a></h3>]]></content>
<summary type="html"><h3 id="版本"><a href="#版本" class="headerlink" title="版本"></a>版本</h3><p>这里使用的是<code>@vue/cli 4.5.13</code>,简单的新建了一个vue2的项目</p>
<h3 id><a href=</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="Vue" scheme="https://yongmaple.com/tags/Vue/"/>
</entry>
<entry>
<title>Vue组件化开发</title>
<link href="https://yongmaple.com/2021/05/18/Vue%E7%BB%84%E4%BB%B6%E5%8C%96%E5%BC%80%E5%8F%91/"/>
<id>https://yongmaple.com/2021/05/18/Vue%E7%BB%84%E4%BB%B6%E5%8C%96%E5%BC%80%E5%8F%91/</id>
<published>2021-05-18T16:14:49.000Z</published>
<updated>2021-08-29T13:38:44.220Z</updated>
<content type="html"><![CDATA[<h3 id="组件通信常用方式"><a href="#组件通信常用方式" class="headerlink" title="组件通信常用方式"></a>组件通信常用方式</h3><ul><li>props</li><li>$emit/$on</li><li>event bus</li><li>vuex</li></ul><h3 id="边界情况"><a href="#边界情况" class="headerlink" title="边界情况"></a>边界情况</h3><ul><li>$parent</li><li>$children</li><li>$root</li><li>$refs</li><li>provide/inject</li><li>非 prop 特性<ul><li>$attrs</li><li>$listeners</li></ul></li></ul><h4 id="事件总线的实现方式"><a href="#事件总线的实现方式" class="headerlink" title="事件总线的实现方式"></a>事件总线的实现方式</h4><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"><span class="class"><span class="keyword">class</span> <span class="title">Bus</span> </span>{</span><br><span class="line"> <span class="function"><span class="title">constructor</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="built_in">this</span>.callbacks = {}</span><br><span class="line"> }</span><br><span class="line"> $on(name, fn) {</span><br><span class="line"> <span class="built_in">this</span>.callbacks[name] = <span class="built_in">this</span>.callbacks[name] || []</span><br><span class="line"> <span class="built_in">this</span>.callbacks[name].push(fn)</span><br><span class="line"> }</span><br><span class="line"> $emit(name, args) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">this</span>.callbacks[name]) {</span><br><span class="line"> <span class="built_in">this</span>.callbacks[name].forEach(<span class="function"><span class="params">cb</span> =></span> cb(args))</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// main.js</span></span><br><span class="line">Vue.prototype.$bus = <span class="keyword">new</span> Bus()</span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="comment">// child1</span></span><br><span class="line"><span class="built_in">this</span>.$bus.$on(<span class="string">'foo'</span>, handle)</span><br><span class="line"><span class="comment">// chiild2</span></span><br><span class="line"><span class="built_in">this</span>.$bus.$emit(<span class="string">'foo)</span></span><br></pre></td></tr></table></figure><p>通常直接用 Vue 代替 Bus,因为 Vue 已经实现了$on和$emit</p><h4 id="parent-root"><a href="#parent-root" class="headerlink" title="$parent/$root"></a>$parent/$root</h4><p>兄弟组件之间通信可通过共同祖辈搭桥,$parent或$root</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="comment">// brother1</span></span><br><span class="line"><span class="built_in">this</span>.$parent.$on(<span class="string">'foo'</span>, handle)</span><br><span class="line"><span class="comment">// brother2</span></span><br><span class="line"><span class="built_in">this</span>.$parent.$emit(<span class="string">'foo'</span>)</span><br></pre></td></tr></table></figure><h4 id="children"><a href="#children" class="headerlink" title="$children"></a>$children</h4><p>父组件可以通过$children 访问子组件实现父子通信</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="comment">// parent</span></span><br><span class="line"><span class="built_in">this</span>.$children[<span class="number">0</span>].xx = <span class="string">'xxx'</span></span><br><span class="line"><span class="comment">// $children不能保证子元素顺序</span></span><br></pre></td></tr></table></figure><h4 id="refs"><a href="#refs" class="headerlink" title="$refs"></a>$refs</h4><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="comment">// parent</span></span><br><span class="line"><HelloWorld ref=<span class="string">'hw'</span>></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">mounted</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="built_in">this</span>.$refs.hw.xx = <span class="string">'xxx'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="attrs-listeners"><a href="#attrs-listeners" class="headerlink" title="$attrs/$listeners"></a>$attrs/$listeners</h4><p>包含了父作用域中<em>不作为 prop 被识别</em>的特性绑定(class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定(class 和 style 除外),并且可以通过<code>v-bind="$attrs"</code>传入内部组件,在创建高级别的组件时非常有用</p><p>非属性特性:没在 props 中声明,但是使用了的属性</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="comment">// child:并未在props中声明foo</span></span><br><span class="line"><p>{{$attrs.foo}}</p></span><br><span class="line"><span class="comment">// parent</span></span><br><span class="line"><HelloWorld foo=<span class="string">"foo"</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">// 给Grandson隔代传值</span></span><br><span class="line"><Child2 msg=<span class="string">"lalala"</span> @some-event=<span class="string">"onSomeEvent"</span>></Child2></span><br><span class="line"><span class="comment">// child2做展开</span></span><br><span class="line"><Grandson v-bind=<span class="string">"$attrs"</span> v-on=<span class="string">"$listeners"</span>></Grandson></span><br><span class="line"><span class="comment">// Grandson使用</span></span><br><span class="line"><div @click=<span class="string">"$emit('some-event', 'msg from grandson')"</span>>{{msg}}</div></span><br></pre></td></tr></table></figure><p><strong>可以使用<code>inheritAttrs: false</code>防止属性透传</strong></p><h4 id="provide-inject"><a href="#provide-inject" class="headerlink" title="provide/inject"></a>provide/inject</h4><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="comment">//</span></span><br><span class="line"><span class="function"><span class="title">provide</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> { <span class="attr">foo</span>: <span class="string">'foo'</span> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">//</span></span><br><span class="line">inject: [<span class="string">'foo'</span>]</span><br></pre></td></tr></table></figure><p>官方中 provide/inject 不是响应式的,如果想实现响应式,只需要传入 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">provide</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> grandpa: <span class="built_in">this</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>inject 优先级高于 prop(inject > props > methods > data),如果重名,会报错</p><p>重命名及默认值</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">inject: {</span><br><span class="line"> foo2: <span class="string">'foo'</span></span><br><span class="line">}</span><br><span class="line">inject: {</span><br><span class="line"> foo: {</span><br><span class="line"> <span class="keyword">from</span>: <span class="string">'foo'</span>,</span><br><span class="line"> <span class="keyword">default</span>: <span class="string">'default foo'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="插槽"><a href="#插槽" class="headerlink" title="插槽"></a>插槽</h3><p>插槽语法是 Vue 实现的内容分发 API,用于复合组件开发。该技术在通用组件库开发中有大量应用。</p><h4 id="匿名插槽"><a href="#匿名插槽" class="headerlink" title="匿名插槽"></a>匿名插槽</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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">// comp1</span></span><br><span class="line"><div></span><br><span class="line"> <slot></slot></span><br><span class="line"></div></span><br><span class="line"></span><br><span class="line"><span class="comment">// parent</span></span><br><span class="line"><comp>hello</comp></span><br></pre></td></tr></table></figure><h4 id="具名插槽"><a href="#具名插槽" class="headerlink" title="具名插槽"></a>具名插槽</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// comp2</span></span><br><span class="line"><div></span><br><span class="line"> <slot></slot></span><br><span class="line"> <slot name=<span class="string">"content"</span>></slot></span><br><span class="line"></div></span><br><span class="line"><span class="comment">// parent</span></span><br><span class="line"><comp2></span><br><span class="line"> <!-- 默认插槽用<span class="keyword">default</span>做参数 --></span><br><span class="line"> <template v-slot:<span class="keyword">default</span>>具名插槽</template></span><br><span class="line"> <!-- 具名插槽用插槽名做参数 --></span><br><span class="line"> <template v-slot:content>内容</template></span><br><span class="line"></comp2></span><br></pre></td></tr></table></figure><h4 id="作用域插槽"><a href="#作用域插槽" class="headerlink" title="作用域插槽"></a>作用域插槽</h4><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// comp3</span></span><br><span class="line"><div></span><br><span class="line"> <slot :foo=<span class="string">"foo"</span>></slot></span><br><span class="line"></div></span><br><span class="line"><span class="comment">// parent</span></span><br><span class="line"><comp3></span><br><span class="line"> <!-- 把v-slot的值指定为作用域上下文对象 --></span><br><span class="line"> <template v-slot:<span class="keyword">default</span>=<span class="string">"slotProps"</span>></span><br><span class="line"> 来自子组件数据:{{slotProps.foo}}</span><br><span class="line"> </template></span><br><span class="line"></comp3></span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h3><h4 id="input"><a href="#input" class="headerlink" title="input"></a>input</h4><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></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="comment"> 1.:value</span></span><br><span class="line"><span class="comment"> 2.@input --></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">:type</span>=<span class="string">"type"</span> <span class="attr">:value</span>=<span class="string">"value"</span> @<span class="attr">input</span>=<span class="string">"onInput"</span> <span class="attr">v-bind</span>=<span class="string">"$attrs"</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><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">export</span> <span class="keyword">default</span> {</span></span><br><span class="line"><span class="javascript"> inheritAttrs: <span class="literal">false</span>,</span></span><br><span class="line"> props: {</span><br><span class="line"> value: {</span><br><span class="line"><span class="javascript"> type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="javascript"> required: <span class="literal">true</span>,</span></span><br><span class="line"> },</span><br><span class="line"> type: {</span><br><span class="line"><span class="javascript"> type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="javascript"> <span class="keyword">default</span>: <span class="string">'text'</span>,</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">onInput</span>(<span class="params">e</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.$emit(<span class="string">'input'</span>, e.target.value)</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="comment">// 通知校验</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.$parent.$emit(<span class="string">'validate'</span>)</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><h4 id="formItem"><a href="#formItem" class="headerlink" title="formItem"></a>formItem</h4><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></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"><!-- 1.label --></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">v-if</span>=<span class="string">"label"</span>></span>{{ label }}<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="comment"><!-- 1.5容器 --></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="comment"><!-- 2.校验错误信息 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">"error"</span>></span>{{ error }}<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">template</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">import</span> Validator <span class="keyword">from</span> <span class="string">'async-validator'</span></span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="keyword">export</span> <span class="keyword">default</span> {</span></span><br><span class="line"><span class="javascript"> inject: [<span class="string">'form'</span>],</span></span><br><span class="line"> props: {</span><br><span class="line"> label: {</span><br><span class="line"><span class="javascript"> type: <span class="built_in">String</span>,</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> prop: <span class="built_in">String</span>,</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> error: <span class="string">''</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mounted</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.$on(<span class="string">'validate'</span>, <span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.validate()</span></span><br><span class="line"> })</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">validate</span>(<span class="params"></span>)</span> {</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">const</span> value = <span class="built_in">this</span>.form.model[<span class="built_in">this</span>.prop]</span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> rules = <span class="built_in">this</span>.form.rules[<span class="built_in">this</span>.prop]</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> validator = <span class="keyword">new</span> Validator({ [<span class="built_in">this</span>.prop]: rules })</span></span><br><span class="line"><span class="javascript"> <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></span><br><span class="line"><span class="javascript"> validator.validate({ [<span class="built_in">this</span>.prop]: value }, <span class="function">(<span class="params">errors</span>) =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(errors)</span></span><br><span class="line"> if (errors) {</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.error = errors[<span class="number">0</span>].message</span></span><br><span class="line"> reject()</span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.error = <span class="string">''</span></span></span><br><span class="line"> resolve()</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="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><h4 id="form"><a href="#form" class="headerlink" title="form"></a>form</h4><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></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="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><span class="line"><span class="tag"></<span class="name">template</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">export</span> <span class="keyword">default</span> {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">provide</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> form: <span class="built_in">this</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> props: {</span><br><span class="line"> model: {</span><br><span class="line"><span class="javascript"> type: <span class="built_in">Object</span>,</span></span><br><span class="line"><span class="javascript"> required: <span class="literal">true</span>,</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> rules: <span class="built_in">Object</span>,</span></span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">validate</span>(<span class="params">cb</span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 1.获取所有items,执行他们的validate,统一处理返回Promise</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> results = <span class="built_in">this</span>.$children</span></span><br><span class="line"><span class="javascript"> .filter(<span class="function">(<span class="params">item</span>) =></span> item.prop)</span></span><br><span class="line"><span class="javascript"> .map(<span class="function">(<span class="params">item</span>) =></span> item.validate())</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(results)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">Promise</span>.all(results)</span></span><br><span class="line"><span class="javascript"> .then(<span class="function">() =></span> cb(<span class="literal">true</span>))</span></span><br><span class="line"><span class="javascript"> .catch(<span class="function">() =></span> cb(<span class="literal">false</span>))</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><h4 id="使用-form"><a href="#使用-form" class="headerlink" title="使用 form"></a>使用 form</h4><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></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="tag"><<span class="name">Form</span> <span class="attr">:model</span>=<span class="string">"model"</span> <span class="attr">:rules</span>=<span class="string">"rules"</span> <span class="attr">ref</span>=<span class="string">"loginForm"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">FormItem</span> <span class="attr">label</span>=<span class="string">"用户名"</span> <span class="attr">prop</span>=<span class="string">"username"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Input</span> <span class="attr">v-model</span>=<span class="string">"model.username"</span> <span class="attr">placeholder</span>=<span class="string">"请输入用户名"</span>></span><span class="tag"></<span class="name">Input</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">FormItem</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">FormItem</span> <span class="attr">label</span>=<span class="string">"密码"</span> <span class="attr">prop</span>=<span class="string">"password"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Input</span> <span class="attr">v-model</span>=<span class="string">"model.password"</span> <span class="attr">placeholder</span>=<span class="string">"请输入"</span>></span><span class="tag"></<span class="name">Input</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">FormItem</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">FormItem</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"onLogin"</span>></span>登录<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">FormItem</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">Form</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><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> Input <span class="keyword">from</span> <span class="string">"@/components/form/Input.vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> FormItem <span class="keyword">from</span> <span class="string">"@/components/form/FormItem.vue"</span>;</span></span><br><span class="line"><span class="javascript"><span class="keyword">import</span> Form <span class="keyword">from</span> <span class="string">"@/components/form/Form.vue"</span>;</span></span><br><span class="line"></span><br><span class="line"><span class="javascript"><span class="keyword">export</span> <span class="keyword">default</span> {</span></span><br><span class="line"> components: {</span><br><span class="line"> Input,</span><br><span class="line"> FormItem,</span><br><span class="line"> Form</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"> model: {</span><br><span class="line"><span class="javascript"> username: <span class="string">"tom"</span>,</span></span><br><span class="line"><span class="javascript"> password: <span class="string">''</span></span></span><br><span class="line"> },</span><br><span class="line"> rules: {</span><br><span class="line"><span class="javascript"> username: [{ <span class="attr">required</span>: <span class="literal">true</span>, <span class="attr">message</span>: <span class="string">"用户名为必填项"</span> }],</span></span><br><span class="line"><span class="javascript"> password: [{ <span class="attr">required</span>: <span class="literal">true</span>, <span class="attr">message</span>: <span class="string">"密码为必填项"</span> }],</span></span><br><span class="line"> },</span><br><span class="line"> };</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">onLogin</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.$refs.loginForm.validate(<span class="function">(<span class="params">isValidate</span>) =></span> {</span></span><br><span class="line"> if (isValidate) {</span><br><span class="line"><span class="javascript"> <span class="comment">// 通过</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'submit login'</span>);</span></span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="javascript"> alert(<span class="string">'校验失败,请重试!'</span>)</span></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="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>在 input 中,这里使用的是<code>this.$parent.$emit('validate')</code>来处理的,这里不够健壮,在 ElementUI 中,使用了 dispatch 来处理的</p><p><a href="https://github.com/ElemeFE/element/blob/HEAD/src/mixins/emitter.js">https://github.com/ElemeFE/element/blob/HEAD/src/mixins/emitter.js</a></p><p><img src="/2021/05/18/Vue%E7%BB%84%E4%BB%B6%E5%8C%96%E5%BC%80%E5%8F%91/2.png" alt="ElementUI源码"></p><p>在 dispatch 中,是递归向上查找父组件派发的,如果用在目前的例子中,就是如下写法:</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="built_in">this</span>.dispatch(<span class="string">'FormItem'</span>, <span class="string">'validate'</span>)</span><br></pre></td></tr></table></figure><p>源码中的使用:<a href="https://github.com/ElemeFE/element/blob/HEAD/packages/input/src/input.vue">https://github.com/ElemeFE/element/blob/HEAD/packages/input/src/input.vue</a></p><p><img src="/2021/05/18/Vue%E7%BB%84%E4%BB%B6%E5%8C%96%E5%BC%80%E5%8F%91/3.png" alt="ElementUI源码"></p><p>dispatch 是通过 mixin 引入的</p><p>可以在源码中看一下 created 中对 prop 的处理。formItem 创建后发现有 prop 属性需要做校验,就 dispatch 一个<code>el.form.addField</code>事件,相当于注册。如果要执行校验,就直接访问 fields<br><a href="https://github.com/ElemeFE/element/blob/HEAD/packages/form/src/form.vue">https://github.com/ElemeFE/element/blob/HEAD/packages/form/src/form.vue</a></p><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><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">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">create</span>(<span class="params">Component, props</span>) </span>{</span><br><span class="line"> <span class="comment">// 创建Component组件实例</span></span><br><span class="line"> <span class="keyword">const</span> Ctor = Vue.extend(Component)</span><br><span class="line"> <span class="keyword">const</span> comp = <span class="keyword">new</span> Ctor({</span><br><span class="line"> propsData: props,</span><br><span class="line"> })</span><br><span class="line"> <span class="comment">// 挂载</span></span><br><span class="line"> <span class="comment">// 这里需要注意,不能直接comp.$mount(document.body),因为$mount实际上是在执行替换操作,会导致页面空白</span></span><br><span class="line"> comp.$mount()</span><br><span class="line"> <span class="comment">// 手动追加</span></span><br><span class="line"> <span class="built_in">document</span>.body.appendChild(comp.$el)</span><br><span class="line"> <span class="comment">// 销毁方法</span></span><br><span class="line"> comp.remove = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">document</span>.body.removeChild(comp.$el)</span><br><span class="line"> comp.$destroy()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> comp</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>写个通知组件</p><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></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 class="attr">class</span>=<span class="string">"box"</span> <span class="attr">v-if</span>=<span class="string">"isShow"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>{{title}}<span class="tag"></<span class="name">h3</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"box-content"</span>></span>{{message}}<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">template</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">export</span> <span class="keyword">default</span> {</span></span><br><span class="line"> props: {</span><br><span class="line"> title: {</span><br><span class="line"><span class="javascript"> type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="javascript"> <span class="keyword">default</span>: <span class="string">''</span>,</span></span><br><span class="line"> },</span><br><span class="line"> message: {</span><br><span class="line"><span class="javascript"> type: <span class="built_in">String</span>,</span></span><br><span class="line"><span class="javascript"> <span class="keyword">default</span>: <span class="string">''</span>,</span></span><br><span class="line"> },</span><br><span class="line"> duration: {</span><br><span class="line"><span class="javascript"> type: <span class="built_in">Number</span>,</span></span><br><span class="line"><span class="javascript"> <span class="keyword">default</span>: <span class="number">1000</span>,</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> isShow: <span class="literal">false</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">show</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.isShow = <span class="literal">true</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">setTimeout</span>(<span class="built_in">this</span>.hide, <span class="built_in">this</span>.duration)</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">hide</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.isShow = <span class="literal">false</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.remove()</span></span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>调用</p><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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">import</span> create <span class="keyword">from</span> <span class="string">'util/create'</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">import</span> Notice <span class="keyword">from</span> <span class="string">'components/Notice.vue'</span></span></span><br><span class="line"></span><br><span class="line"> ...</span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> notice = create(Notice, {</span></span><br><span class="line"><span class="javascript"> title: <span class="string">'标题'</span>,</span></span><br><span class="line"><span class="javascript"> message: <span class="string">'成功'</span>,</span></span><br><span class="line"> duration: 2000</span><br><span class="line"> })</span><br><span class="line"> notice.show()</span><br><span class="line"> ...</span><br><span class="line"><span class="tag"></<span class="name">script</span>></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></pre></td><td class="code"><pre><span class="line"><span class="built_in">this</span>.$notice({</span><br><span class="line"> title: <span class="string">'标题'</span>,</span><br><span class="line"> message: <span class="string">'成功'</span>,</span><br><span class="line"> duration: <span class="number">2000</span>,</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>只需要在 main.js 中注册$notice 就可以了</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">Vue.prototype.$notice = <span class="function"><span class="keyword">function</span> (<span class="params">props</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> notice = create(Notice, props)</span><br><span class="line"> notice.show()</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>elementUI 中就是用这种方式注册</p><p><a href="https://github.com/ElemeFE/element/blob/dev/src/index.js">https://github.com/ElemeFE/element/blob/dev/src/index.js</a></p><p><img src="/2021/05/18/Vue%E7%BB%84%E4%BB%B6%E5%8C%96%E5%BC%80%E5%8F%91/1.png" alt="elementUI源码"></p>]]></content>
<summary type="html"><h3 id="组件通信常用方式"><a href="#组件通信常用方式" class="headerlink" title="组件通信常用方式"></a>组件通信常用方式</h3><ul>
<li>props</li>
<li>$emit/$on</li>
<li>event </summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="Vue" scheme="https://yongmaple.com/tags/Vue/"/>
<category term="组件化" scheme="https://yongmaple.com/tags/%E7%BB%84%E4%BB%B6%E5%8C%96/"/>
</entry>
<entry>
<title>110数字图像编码</title>
<link href="https://yongmaple.com/2021/05/18/110%E6%95%B0%E5%AD%97%E5%9B%BE%E5%83%8F%E7%BC%96%E7%A0%81/"/>
<id>https://yongmaple.com/2021/05/18/110%E6%95%B0%E5%AD%97%E5%9B%BE%E5%83%8F%E7%BC%96%E7%A0%81/</id>
<published>2021-05-18T15:31:58.000Z</published>
<updated>2021-08-29T13:38:44.136Z</updated>
<content type="html"><![CDATA[<h3 id="数字-110-图像编码:"><a href="#数字-110-图像编码:" class="headerlink" title="数字 110 图像编码:"></a>数字 110 图像编码:</h3><span id="more"></span><p>0:铁圈<br>1:铅笔<br>2:鸭子<br>3:耳朵<br>4:红旗<br>5:手<br>6:口哨<br>7:拐杖<br>8:葫芦<br>9:勺子<br>10:棒球<br>11:筷子<br>12:婴儿<br>13:医生<br>14:钥匙<br>15:鹦鹉<br>16:杨柳<br>17:陈浩南<br>18:尾巴<br>19:药酒<br>20:耳环<br>21:阿姨<br>22:鸳鸯<br>23:耳塞<br>24:耳饰<br>25:二胡<br>26:二流<br>27:耳机<br>28:二八大杠自行车<br>29:二舅<br>30:山洞<br>31:鲨鱼<br>32:扇儿<br>33:仙丹<br>34:绅士<br>35:珊瑚<br>36:山路<br>37:山鸡<br>38:沙发<br>39:三角<br>40:司令<br>41:司仪<br>42:柿儿<br>43:石山<br>44:石狮<br>45:师傅<br>46:石榴<br>47:司机<br>48:扫把<br>49:石球<br>50:五环<br>51:狐狸<br>52:孤儿<br>53:牡丹<br>54:武士<br>55:木屋<br>56:蜗牛<br>57:武器<br>58:苦瓜<br>59:五角<br>60:榴莲<br>61:轮椅<br>62:女儿<br>63:刘三姐<br>64:律师<br>65:老虎<br>66:溜溜球<br>67:楼梯<br>68:喇叭<br>69:牛角<br>70:麒麟<br>71:蜥蜴<br>72:企鹅<br>73:鸡蛋<br>74:骑士<br>75:积木<br>76:气流<br>77:蛐蛐<br>78:青蛙<br>79:气球<br>80:百灵<br>81:白蚁<br>82:靶儿<br>83:华山<br>84:消毒液<br>85:白虎<br>86:八路<br>87:白痴<br>88:爸爸<br>89:白酒<br>90:精灵<br>91:九姨太<br>92:球儿<br>93:救生圈<br>94:教师<br>95:酒壶<br>96:旧炉<br>97:酒席<br>98:酒吧<br>99:舅舅<br>00:望远镜<br>01:羚羊<br>02:铃儿<br>03:灵山<br>04:零食<br>05:灵符<br>06:灵力<br>07:令旗<br>08:篱笆<br>09:灵柩</p><h3 id="字母数字声母对应系统"><a href="#字母数字声母对应系统" class="headerlink" title="字母数字声母对应系统"></a>字母数字声母对应系统</h3><p><img src="/2021/05/18/110%E6%95%B0%E5%AD%97%E5%9B%BE%E5%83%8F%E7%BC%96%E7%A0%81/1.png" alt="字母数字声母对应"></p><h3 id="声母对应数字的记忆:"><a href="#声母对应数字的记忆:" class="headerlink" title="声母对应数字的记忆:"></a>声母对应数字的记忆:</h3><p>0—D:0 象形 D。<br>1—y:1 的发音首字母是 y。<br>2—Z:象形。<br>3—S:3 的手音首字母是 S。<br>4—h:倒象形。<br>5—w:5 的手音首字母是 W。<br>6—g:倒象形。<br>7—t:象形。<br>8—b:象形。<br>9—q:象形。</p><h3 id="千位数字宫殿编码制作范例"><a href="#千位数字宫殿编码制作范例" class="headerlink" title="千位数字宫殿编码制作范例"></a>千位数字宫殿编码制作范例</h3><p>123 对应字母:yzs=椅子上=坐垫。<br>546 对应字母:whg=无花果。<br>457 对应字母:hwt 话务台=电话。<br>965 对应字母:qgw 情歌王=情歌王子张信哲。<br>786 对应字母:tbg 特别高=姚明。</p><h3 id="千位数字宫殿记忆信息范例"><a href="#千位数字宫殿记忆信息范例" class="headerlink" title="千位数字宫殿记忆信息范例"></a>千位数字宫殿记忆信息范例</h3><p>123 椅子上=坐垫。<br>124 一直挥=旗帜。<br>125 杨宗纬。<br>126 一只狗。<br>127 圆柱体。<br>128 圆珠笔。</p><h3 id="千位数字宫殿随机成语记忆"><a href="#千位数字宫殿随机成语记忆" class="headerlink" title="千位数字宫殿随机成语记忆"></a>千位数字宫殿随机成语记忆</h3><p>相亲相爱——123(yzs)椅子上=坐垫联想:坐垫上有两个人在相亲相爱。</p>]]></content>
<summary type="html"><h3 id="数字-110-图像编码:"><a href="#数字-110-图像编码:" class="headerlink" title="数字 110 图像编码:"></a>数字 110 图像编码:</h3></summary>
<category term="记忆" scheme="https://yongmaple.com/categories/%E8%AE%B0%E5%BF%86/"/>
<category term="记忆宫殿" scheme="https://yongmaple.com/tags/%E8%AE%B0%E5%BF%86%E5%AE%AB%E6%AE%BF/"/>
</entry>
<entry>
<title>Vue面试题</title>
<link href="https://yongmaple.com/2021/04/26/Vue%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<id>https://yongmaple.com/2021/04/26/Vue%E9%9D%A2%E8%AF%95%E9%A2%98/</id>
<published>2021-04-26T15:05:30.000Z</published>
<updated>2021-08-29T13:38:44.220Z</updated>
<content type="html"><![CDATA[<h3 id="1-v-if-和-v-for-哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?"><a href="#1-v-if-和-v-for-哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?" class="headerlink" title="1. v-if 和 v-for 哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?"></a>1. v-if 和 v-for 哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?</h3><h4 id="简答:"><a href="#简答:" class="headerlink" title="简答:"></a>简答:</h4><ul><li>v-for 优先级更高</li><li>将 v-if 提取出来,可以用<code><template v-if="condition"><div v-for="item in items"></div></template></code></li></ul><h4 id="分析:"><a href="#分析:" class="headerlink" title="分析:"></a>分析:</h4><p>一个简单的例子,打印出渲染函数</p><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></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">div</span> <span class="attr">id</span>=<span class="string">"demo"</span>></span></span><br><span class="line"> <span class="comment"><!-- <p v-if="isShow" v-for="item in list">{{item.name}}</p> --></span></span><br><span class="line"> <span class="tag"><<span class="name">template</span> <span class="attr">v-if</span>=<span class="string">"isShow"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">v-for</span>=<span class="string">"item in list"</span>></span>{{item.name}}<span class="tag"></<span class="name">p</span>></span></span><br><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="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"../../dist//vue.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = <span class="keyword">new</span> Vue({</span></span><br><span class="line"><span class="javascript"> el: <span class="string">'#demo'</span>,</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"> list: [</span><br><span class="line"> {</span><br><span class="line"><span class="javascript"> name: <span class="string">'foo'</span>,</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"><span class="javascript"> name: <span class="string">'bar'</span>,</span></span><br><span class="line"> },</span><br><span class="line"> ],</span><br><span class="line"><span class="javascript"> isShow: <span class="literal">true</span>,</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(app.$options.render)</span></span><br><span class="line"> <span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>此为不用 template 提取 v-if 的结果<br>其中<code>_l</code>是列表渲染函数,可以看出,在外层先执行了<code>_l</code>,再在其内部执行 isShow 的判断</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">ƒ anonymous(</span><br><span class="line">) {</span><br><span class="line"><span class="function"><span class="title">with</span>(<span class="params"><span class="built_in">this</span></span>)</span>{<span class="keyword">return</span> _c(<span class="string">'div'</span>,{<span class="attr">attrs</span>:{<span class="string">"id"</span>:<span class="string">"demo"</span>}},_l((list),<span class="function"><span class="keyword">function</span>(<span class="params">item</span>)</span>{<span class="keyword">return</span> (isShow)?_c(<span class="string">'p'</span>,[_v(_s(item.name))]):_e()}),<span class="number">0</span>)}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>此为使用 template 提取 v-if 的结果<br>外层先进行了 isShow 的判断,再执行内部的<code>_l</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></pre></td><td class="code"><pre><span class="line">ƒ anonymous(</span><br><span class="line">) {</span><br><span class="line"><span class="function"><span class="title">with</span>(<span class="params"><span class="built_in">this</span></span>)</span>{<span class="keyword">return</span> _c(<span class="string">'div'</span>,{<span class="attr">attrs</span>:{<span class="string">"id"</span>:<span class="string">"demo"</span>}},[(isShow)?_l((list),<span class="function"><span class="keyword">function</span>(<span class="params">item</span>)</span>{<span class="keyword">return</span> _c(<span class="string">'p'</span>,[_v(_s(item.name))])}):_e()],<span class="number">2</span>)}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>源码位置:<code>src/compiler/codegen/index.js</code></p><p><img src="/2021/04/26/Vue%E9%9D%A2%E8%AF%95%E9%A2%98/1.png" alt="genElement"></p><p>源码中就可以很明显的看到 v-for 是在 v-if 前面的</p><p><strong>如果需要根据 item 内属性的值来做 v-if,可以使用<code>computed</code>过滤出一个删选后的 list,再执行 v-for,如此便可提升性能</strong></p><h4 id="结论:"><a href="#结论:" class="headerlink" title="结论:"></a>结论:</h4><ol><li>显然 v-for 优先于 v-if 被解析</li><li>如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能</li><li>要避免同时出现,则在外层嵌套<code>template</code>,在这一层进行 v-if 判断,然后内部进行 v-for</li></ol><h3 id="2-Vue-组件-data-为什么必须是个函数而-Vue-的根实例则没有此限制?"><a href="#2-Vue-组件-data-为什么必须是个函数而-Vue-的根实例则没有此限制?" class="headerlink" title="2. Vue 组件 data 为什么必须是个函数而 Vue 的根实例则没有此限制?"></a>2. Vue 组件 data 为什么必须是个函数而 Vue 的根实例则没有此限制?</h3><h4 id="简答:-1"><a href="#简答:-1" class="headerlink" title="简答:"></a>简答:</h4><ul><li>为了保证在多实例的时候状态相互不干扰</li><li>根实例只会有一个,所以不担心状态污染</li></ul><h4 id="分析:-1"><a href="#分析:-1" class="headerlink" title="分析:"></a>分析:</h4><p>如果组件中 data 是个对象,此时会报错</p><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></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">div</span> <span class="attr">id</span>=<span class="string">"demo"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">comp</span>></span><span class="tag"></<span class="name">comp</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>{{title}}<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">script</span> <span class="attr">src</span>=<span class="string">"../../dist//vue.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> Vue.component(<span class="string">'comp'</span>, {</span></span><br><span class="line"><span class="handlebars"><span class="xml"> template: `<span class="tag"><<span class="name">div</span> @<span class="attr">click</span>=<span class="string">"counter++"</span>></span></span><span class="template-variable">{{<span class="name">counter</span>}}</span><span class="xml"><span class="tag"></<span class="name">div</span>></span>`,</span></span></span><br><span class="line"> data: {</span><br><span class="line"> counter: 1,</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="comment">// data() {</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// return {</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// counter: 1</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// }</span></span></span><br><span class="line"><span class="javascript"> <span class="comment">// }</span></span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = <span class="keyword">new</span> Vue({</span></span><br><span class="line"><span class="javascript"> el: <span class="string">'#demo'</span>,</span></span><br><span class="line"> data: {</span><br><span class="line"><span class="javascript"> title: <span class="string">'哈哈'</span>,</span></span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"> <span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p><img src="/2021/04/26/Vue%E9%9D%A2%E8%AF%95%E9%A2%98/2.png" alt="报错"></p><p>源码位置:<code>src/core/instance/state.js</code></p><p><img src="/2021/04/26/Vue%E9%9D%A2%E8%AF%95%E9%A2%98/3.png" alt="initData"></p><p>源码中可以看到如果 data 是函数,则执行,并将其结果作为 data 选项的值,如果不是函数,就直接使用 data</p><p><strong>在组件中 data 使用对象,一个组件的多个实例会共用一个 data,会导致数据污染</strong></p><p>在根实例中使用则不会有问题,因为在创建根实例的时候都是通过<code>new</code>的方式创建的,在全局范围他是单例的,所以不会存在污染</p><p><code>src/core/util/options.js</code></p><p><img src="/2021/04/26/Vue%E9%9D%A2%E8%AF%95%E9%A2%98/4.png" alt="校验"></p><ul><li>数据初始化的时候会去检测 data 的形式,从而去执行具体的执行方式。</li><li>由于根实例在创建的时候,它会有实例,只有根实例才会有,所以它可以躲过关于 data 选项的校验。</li><li>而如果是一个普通的组件,由于它不存在实例,所以它无法躲过 if 逻辑校验</li></ul><h4 id="结论:-1"><a href="#结论:-1" class="headerlink" title="结论:"></a>结论:</h4><ul><li>Vue 组件可能存在多个实例,如果使用对象形式定义 data,则会导致它们共用一个 data 对象,那么状态变更将会影响所有组件实例,这是不合理的。</li><li>采用函数形式定义,在 initData 时会将其作为工厂函数返回全新 data 对象,有效规避多实例之间状态污染问题。</li><li>而在 Vue 根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种问题。</li></ul><h3 id="3-你知道-vue-中-key-的作用和工作原理吗?说说你对他的理解"><a href="#3-你知道-vue-中-key-的作用和工作原理吗?说说你对他的理解" class="headerlink" title="3. 你知道 vue 中 key 的作用和工作原理吗?说说你对他的理解"></a>3. 你知道 vue 中 key 的作用和工作原理吗?说说你对他的理解</h3><h4 id="分析:-2"><a href="#分析:-2" class="headerlink" title="分析:"></a>分析:</h4><p>例子:</p><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">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"demo"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">v-for</span>=<span class="string">"item in list"</span>></span>{{item}}<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">script</span> <span class="attr">src</span>=<span class="string">"../../dist/vue.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> app = <span class="keyword">new</span> Vue({</span></span><br><span class="line"><span class="javascript"> el: <span class="string">'#demo'</span>,</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> {</span></span><br><span class="line"><span class="javascript"> list: [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>, <span class="string">'e'</span>],</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mounted</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.list.splice(<span class="number">2</span>, <span class="number">0</span>, <span class="string">'f'</span>)</span></span><br><span class="line"> }, 2000)</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"> <span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>上面的例子中,没有 key 时<code>updateChildren</code>的执行</p><p><img src="/2021/04/26/Vue%E9%9D%A2%E8%AF%95%E9%A2%98/5.png" alt="没key"></p><p>在没有 key 的情况下,会执行 3 次更新操作,一次插入操作<br>有 key 的情况下,在<code>updateChildren</code>时,只执行了一次插入操作</p><p>最直观的方式就是在<code>src/core/vdom/patch.js</code>的<code>updateChildren</code>方法内打上条件断点,看页面上元素的变化</p><p>算法在计算时,没有 key,就相当于 key 相同(都是 undefined),再判断标签、备注等(大多数情况下可以理解为标签一致就行了),此时标签一致,所以算法会认为这是同一个节点,然后再执行 patch</p><p>patch 的时候会比较新旧是否一致,如果一致不执行更新,不一致就执行更新,所以没 key 时候相当于对 f、c、d 进行了强制更新,最后再把 e 插入</p><p>关于源码中<code>updateChildren</code>的运行机制,移步<a href="https://yongmaple.com/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/">Vue2 源码解析二</a></p><h4 id="结论:-2"><a href="#结论:-2" class="headerlink" title="结论:"></a>结论:</h4><ol><li>key 的作用主要是为了高效的更新虚拟 dom,其原理是 vue 在 patch 过程中通过 key 可以精准判断两个节点是否是用一个,从而避免频繁更新不同元素,使得整个 patch 过程更加高效,减少 dom 操作量,提高性能。</li><li>另外,若不设置 key 还可能在列表更新时引发一些隐蔽的 bug</li><li>vue 中使用相同标签名元素的过渡(transition)切换时,也会使用到 key 属性,其目的也是为了让 vue 可以区分它们,否则 vue 只会替代其内部属性而不会触发过渡效果</li></ol><h3 id="4-怎么理解-vue-中的-diff-算法"><a href="#4-怎么理解-vue-中的-diff-算法" class="headerlink" title="4. 怎么理解 vue 中的 diff 算法"></a>4. 怎么理解 vue 中的 diff 算法</h3><h4 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h4><ol><li>必要性: <code>src/core/instance/lifecycle.js</code>中的 mountComponent</li></ol><ul><li>组件中可能存在很多个 data 中的 key 使用</li></ul><ol start="2"><li>执行方式: <code>src/core/vdom/patch.js</code>中的 patchVnode</li></ol><ul><li>patchVnode 是 diff 发生的地方,整体策略:深度优先,同层比较</li></ul><ol start="3"><li>高效性: <code>src/core/vdom/patch.js</code>中的 updateChildren</li></ol><h4 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h4><ol><li>diff 算法是虚拟 dom 技术的必然产物:通过新旧虚拟 dom 作对比(即 diff),将变化的地方更新在真实 dom 上,另外也需要 diff 高效的执行对比过程,从何降低时间复杂度 O(n)</li><li>vue 2.x 中为了降低 Watcher 粒度,每个组件只有一个 Watcher 与之对应,只有引入 diff 才能精确找到发生变化的地方</li><li>vue 中 diff 执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果 oldVnode 和新的渲染结果 newVnode,此过程称为 patch</li><li>diff 过程整体遵循深度优先、同层比较的策略。两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作。比较两组子节点是算法的重点,首先假设头尾节点可能相同做 4 次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点。借助 key 通常可以非常精确找到相同节点,因此整个 patch 过程非常高效</li></ol><h3 id="5-对-vue-组件化的理解"><a href="#5-对-vue-组件化的理解" class="headerlink" title="5. 对 vue 组件化的理解"></a>5. 对 vue 组件化的理解</h3><h4 id="分析-1"><a href="#分析-1" class="headerlink" title="分析"></a>分析</h4><ol><li>组件化定义</li></ol><ul><li><code>Vue.component('comp', { template: '<div></div>'})</code>全局定义。源码位置:<code>src/core/global-api/assets.js</code></li><li><code><template><div></div></template></code> 单文件组件</li></ul><ol start="2"><li><p>组件化优点</p></li><li><p>组件化实现</p></li></ol>]]></content>
<summary type="html"><h3 id="1-v-if-和-v-for-哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?"><a href="#1-v-if-和-v-for-哪个优先级更高?如果两个同时出现,应该怎么优化得到更好的性能?" class="headerlink" title="</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="Vue" scheme="https://yongmaple.com/tags/Vue/"/>
<category term="面试" scheme="https://yongmaple.com/tags/%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>手写Vue二</title>
<link href="https://yongmaple.com/2021/04/26/%E6%89%8B%E5%86%99Vue%E4%BA%8C/"/>
<id>https://yongmaple.com/2021/04/26/%E6%89%8B%E5%86%99Vue%E4%BA%8C/</id>
<published>2021-04-26T14:51:36.000Z</published>
<updated>2021-08-29T13:38:44.228Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="https://yongmaple.com/2021/04/07/%E6%89%8B%E5%86%99Vue/">手写Vue</a></li><li><a href="https://yongmaple.com/2021/04/26/%E6%89%8B%E5%86%99Vue%E4%BA%8C/">手写Vue二</a></li></ul><p>本文项目地址:<a href="https://github.com/YongMaple/my-vue">https://github.com/YongMaple/my-vue</a></p><h3 id="升级改造"><a href="#升级改造" class="headerlink" title="升级改造"></a>升级改造</h3><p>目的:引入虚拟dom</p>]]></content>
<summary type="html"><p>目录:</p>
<ul>
<li><a href="https://yongmaple.com/2021/04/07/%E6%89%8B%E5%86%99Vue/">手写Vue</a></li>
<li><a href="https://yongmaple.com/2021</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="手撕源码" scheme="https://yongmaple.com/tags/%E6%89%8B%E6%92%95%E6%BA%90%E7%A0%81/"/>
<category term="Vue" scheme="https://yongmaple.com/tags/Vue/"/>
</entry>
<entry>
<title>JS父元素hover显示子元素且移入子元素不隐藏</title>
<link href="https://yongmaple.com/2021/04/25/JS%E7%88%B6%E5%85%83%E7%B4%A0hover%E6%98%BE%E7%A4%BA%E5%AD%90%E5%85%83%E7%B4%A0%E4%B8%94%E7%A7%BB%E5%85%A5%E5%AD%90%E5%85%83%E7%B4%A0%E4%B8%8D%E9%9A%90%E8%97%8F/"/>
<id>https://yongmaple.com/2021/04/25/JS%E7%88%B6%E5%85%83%E7%B4%A0hover%E6%98%BE%E7%A4%BA%E5%AD%90%E5%85%83%E7%B4%A0%E4%B8%94%E7%A7%BB%E5%85%A5%E5%AD%90%E5%85%83%E7%B4%A0%E4%B8%8D%E9%9A%90%E8%97%8F/</id>
<published>2021-04-25T15:16:40.000Z</published>
<updated>2021-08-29T13:38:44.140Z</updated>
<content type="html"><![CDATA[<p>关键方法如下:</p><span id="more"></span><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="keyword">let</span> hoverTime</span><br><span class="line"><span class="keyword">const</span> handleMouseEnter = <span class="function">(<span class="params">index</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (!props.hoverInfo) <span class="keyword">return</span></span><br><span class="line"> <span class="built_in">clearTimeout</span>(hoverTime)</span><br><span class="line"> setHoverIndex(index)</span><br><span class="line"> <span class="keyword">if</span> (props.onHoverChange) props.onHoverChange(index)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> handleMouseLeave = <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">if</span> (!props.hoverInfo) <span class="keyword">return</span></span><br><span class="line"> hoverTime = <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> setHoverIndex(-<span class="number">1</span>)</span><br><span class="line"> }, <span class="number">200</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>原理:</p><p>通过<code>setTimeout</code>延迟隐藏,给用户时间从父元素移动到子元素,移入元素后清空定时器</p>]]></content>
<summary type="html"><p>关键方法如下:</p></summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="前端" scheme="https://yongmaple.com/tags/%E5%89%8D%E7%AB%AF/"/>
<category term="React" scheme="https://yongmaple.com/tags/React/"/>
</entry>
<entry>
<title>JS一键复制</title>
<link href="https://yongmaple.com/2021/04/25/JS%E4%B8%80%E9%94%AE%E5%A4%8D%E5%88%B6/"/>
<id>https://yongmaple.com/2021/04/25/JS%E4%B8%80%E9%94%AE%E5%A4%8D%E5%88%B6/</id>
<published>2021-04-25T15:13:47.000Z</published>
<updated>2021-08-29T13:38:44.140Z</updated>
<content type="html"><![CDATA[<p>一键将数据存在剪切板上</p><p>代码如下:</p><span id="more"></span><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">const</span> handleCopy = <span class="function">(<span class="params">obj</span>) =></span> {</span><br><span class="line"> <span class="comment">// 清除上一次的</span></span><br><span class="line"> <span class="keyword">const</span> removeDom = <span class="built_in">document</span>.getElementById(<span class="string">'copyInput'</span>)</span><br><span class="line"> <span class="keyword">if</span> (removeDom) removeDom.remove()</span><br><span class="line"> <span class="keyword">const</span> text = <span class="built_in">document</span>.createElement(<span class="string">'textarea'</span>)</span><br><span class="line"> text.id = <span class="string">'copyInput'</span></span><br><span class="line"> <span class="keyword">const</span> str = <span class="string">`经度:<span class="subst">${obj.lon}</span></span></span><br><span class="line"><span class="string">纬度:<span class="subst">${obj.lat}</span></span></span><br><span class="line"><span class="string">时间段:<span class="subst">${obj.time}</span>`</span></span><br><span class="line"> text.value = str</span><br><span class="line"> <span class="keyword">const</span> container = <span class="built_in">document</span>.getElementById(<span class="string">'lbsUserListContainer'</span>)</span><br><span class="line"> container?.appendChild(text)</span><br><span class="line"> text.select()</span><br><span class="line"> <span class="built_in">document</span>.execCommand(<span class="string">'copy'</span>)</span><br><span class="line"> text.style.display = <span class="string">'none'</span></span><br><span class="line"> message.success(<span class="string">'复制成功'</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注意:</p><p><code>text.style.display = 'none';</code>隐藏需要在<code>document.execCommand('copy');</code>后,不然无法复制</p>]]></content>
<summary type="html"><p>一键将数据存在剪切板上</p>
<p>代码如下:</p></summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="前端" scheme="https://yongmaple.com/tags/%E5%89%8D%E7%AB%AF/"/>
</entry>
<entry>
<title>实用Chrome调试方法</title>
<link href="https://yongmaple.com/2021/04/21/%E5%AE%9E%E7%94%A8Chrome%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95/"/>
<id>https://yongmaple.com/2021/04/21/%E5%AE%9E%E7%94%A8Chrome%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95/</id>
<published>2021-04-21T14:11:37.000Z</published>
<updated>2021-08-29T13:38:44.224Z</updated>
<content type="html"><![CDATA[<ul><li><p>调试界面中搜索指定文件 <code>cmd⌘ + o</code></p><span id="more"></span><p><img src="/2021/04/21/%E5%AE%9E%E7%94%A8Chrome%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95/1.png"></p></li><li><p>条件断点</p></li></ul><p>右键新建断点时选择<code>Add conditional breakpoint</code>,然后输入判断条件,就可以只在满足条件时才进入断点</p><p><img src="/2021/04/21/%E5%AE%9E%E7%94%A8Chrome%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95/2.png" alt="右键"><br><img src="/2021/04/21/%E5%AE%9E%E7%94%A8Chrome%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95/3.png" alt="判断条件"><br><img src="/2021/04/21/%E5%AE%9E%E7%94%A8Chrome%E8%B0%83%E8%AF%95%E6%96%B9%E6%B3%95/4.png" alt="根据条件进入断点"></p>]]></content>
<summary type="html"><ul>
<li><p>调试界面中搜索指定文件 <code>cmd⌘ + o</code></p></li></ul></summary>
<category term="工具" scheme="https://yongmaple.com/categories/%E5%B7%A5%E5%85%B7/"/>
<category term="Chrome" scheme="https://yongmaple.com/tags/Chrome/"/>
<category term="调试" scheme="https://yongmaple.com/tags/%E8%B0%83%E8%AF%95/"/>
</entry>
<entry>
<title>Vue2源码解析三</title>
<link href="https://yongmaple.com/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/"/>
<id>https://yongmaple.com/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/</id>
<published>2021-04-21T11:22:58.000Z</published>
<updated>2021-08-29T13:38:44.156Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="https://yongmaple.com/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li><li><a href="https://yongmaple.com/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/">Vue2 源码解析二</a></li><li><a href="https://yongmaple.com/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/">Vue2 源码解析三</a></li></ul><p>本文项目地址:<a href="https://github.com/YongMaple/vue">https://github.com/YongMaple/vue</a> 内含测试用代码<code>/examples/test/</code></p><h3 id="组件化机制"><a href="#组件化机制" class="headerlink" title="组件化机制"></a>组件化机制</h3><h4 id="注册"><a href="#注册" class="headerlink" title="注册"></a>注册</h4><ul><li>全局声明 <code>Vue.component()</code></li><li>局部声明 <code>components</code></li></ul><p><code>src/core/global-api/index.js</code></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/1.png" alt="动态注册"></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/2.png" alt="ASSET_TYPES"></p><p>真正注册是在<code>initAssetRegisters</code></p><p><code>src/core/global-api/assets.js</code></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/3.png" alt="initAssetRegisters"></p><h4 id="实例化、挂载"><a href="#实例化、挂载" class="headerlink" title="实例化、挂载"></a>实例化、挂载</h4><p>在控制台打印渲染函数<code>console.log(app.$options.render)</code>可以得到如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">anonymous</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="function"><span class="title">with</span>(<span class="params"><span class="built_in">this</span></span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> _c(<span class="string">'div'</span>,{</span><br><span class="line"> attrs:{<span class="string">"id"</span>:<span class="string">"demo"</span>}</span><br><span class="line"> },[</span><br><span class="line"> _c(<span class="string">'h1'</span>,[_v(<span class="string">"Vue组件化机制"</span>)]),</span><br><span class="line"> _v(<span class="string">" "</span>),</span><br><span class="line"> _c(<span class="string">'comp'</span>)</span><br><span class="line"> ],<span class="number">1</span>)}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里的<code>_c</code>就是之前的<code>createElement</code></p><p><code>src/core/instance/render.js</code></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/4.png" alt="_c"></p><p><code>src/core/vdom/create-element.js</code></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/5.png" alt="createElement"></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/6.png" alt="_createElement"></p><p><code>src/core/vdom/create-component.js</code></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/8.png" alt="createComponent"></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/9.png" alt="installComponentHooks"></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/10.png" alt="hooksToMerge"></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/7.png" alt="componentVNodeHooks"></p><p><code>installComponentHooks</code>之后,创建了虚拟 dom,最终返回 vnode</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 根据组件名称,创建虚拟dom</span></span><br><span class="line"><span class="comment">// comp => vue-component-1-comp</span></span><br><span class="line"><span class="keyword">const</span> name = Ctor.options.name || tag</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> vnode = <span class="keyword">new</span> VNode(</span><br><span class="line"> <span class="string">`vue-component-<span class="subst">${Ctor.cid}</span><span class="subst">${name ? <span class="string">`-<span class="subst">${name}</span>`</span> : <span class="string">''</span>}</span>`</span>,</span><br><span class="line"> data,</span><br><span class="line"> <span class="literal">undefined</span>,</span><br><span class="line"> <span class="literal">undefined</span>,</span><br><span class="line"> <span class="literal">undefined</span>,</span><br><span class="line"> context,</span><br><span class="line"> { Ctor, propsData, listeners, tag, children },</span><br><span class="line"> asyncFactory</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>init 何时执行?根实例执行$mount 时</p><p>流程:</p><ol><li>根实例执行$mount <code>vm.$mount()</code></li><li><code>vm.$mount()</code> 中执行了 <code>mountComponent()</code></li><li><code>mountComponent()</code>中声明了<code>updateComponent()</code>,<code>new Watcher()</code></li><li><code>updateComponent()</code>传入<code>new Watcher()</code>并立刻调用一次</li><li><code>updateComponent()</code> 中执行 <code>render()</code> 得到整棵虚拟 dom 树</li><li><code>render()</code>之后执行 <code>_update()</code></li><li><code>_update()</code> 中执行<code>__patch__()</code>,<code>__patch__</code>是平台特有的补丁函数,用于组件更新</li></ol><p>进入到 patch</p><p><code>src/core/vdom/patch.js</code></p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/11.png" alt="patch"></p><p>patch 中使用了 createElm 递归创建 dom 树</p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/12.png" alt="createElm"></p><p>如果是一个组件,调用 createComponent</p><p><img src="/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/13.png" alt="createComponent"></p><ul><li>问:有 parent、child 父子嵌套组件,请问生命周期顺序</li><li>答:parent.created => child.created => child.mounted => parent.mounted 类似于洋葱模型,先进后出</li></ul><p><strong>全文完</strong></p>]]></content>
<summary type="html"><p>目录:</p>
<ul>
<li><a href="https://yongmaple.com/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li>
<li><a href="htt</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="手撕源码" scheme="https://yongmaple.com/tags/%E6%89%8B%E6%92%95%E6%BA%90%E7%A0%81/"/>
<category term="Vue" scheme="https://yongmaple.com/tags/Vue/"/>
</entry>
<entry>
<title>Vue2源码解析二</title>
<link href="https://yongmaple.com/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/"/>
<id>https://yongmaple.com/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/</id>
<published>2021-04-15T16:00:05.000Z</published>
<updated>2021-08-29T13:38:44.168Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="https://yongmaple.com/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li><li><a href="https://yongmaple.com/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/">Vue2 源码解析二</a></li><li><a href="https://yongmaple.com/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/">Vue2 源码解析三</a></li></ul><p>本文项目地址:<a href="https://github.com/YongMaple/vue">https://github.com/YongMaple/vue</a> 内含测试用代码<code>/examples/test/</code></p><h3 id="异步更新队列"><a href="#异步更新队列" class="headerlink" title="异步更新队列"></a>异步更新队列</h3><p>先了解一下<code>Event Loop</code></p><p>推荐阅读:</p><ul><li><a href="https://segmentfault.com/a/1190000016278115">https://segmentfault.com/a/1190000016278115</a></li><li><a href="https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly">https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/?utm_source=html5weekly</a></li></ul><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/1.png" alt="Event Loop"></p><ul><li>Task Queue 宏任务队列</li><li>Microtask Queue 微任务队列</li><li>Call Stack 调用栈</li><li>浏览器端的宏任务:setTimeout、setInterval、requestAnimationFrame、I/O、UI rendering</li><li>浏览器端的微任务:Promise、Object.observe、MutationObserver</li></ul><h5 id="简单总结下浏览器下的-Event-loop-流程:"><a href="#简单总结下浏览器下的-Event-loop-流程:" class="headerlink" title="简单总结下浏览器下的 Event loop 流程:"></a>简单总结下浏览器下的 Event loop 流程:</h5><ol><li>先走完全局同步任务</li><li>然后走完当前所有的微任务</li><li>执行 UI rendering</li><li>走队列第一个宏任务</li><li>走完当前所有的微任务</li><li>执行 UI rendering</li><li>如此循环,如果在微任务执行期间,出现了新的微任务,直接跟在这次微任务队列的末尾(在下次宏任务之前执行)</li></ol><p>Vue 中的具体实现</p><p><strong>nextTick: Vue 尝试使用 nextTick 执行一个异步任务</strong></p><p><strong>Vue 在组件更新的时候是批量的组件更新,把 watcher 放到 queue 里面,等刷新微任务队列的时候,一起执行</strong></p><ul><li>异步:只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更</li><li>批量:如果同一个 watcher 被多次触发,只会被推入到队列中一次。去重对于避免不必要的计算和 DOM 操作是非常重要的。然后在下一个事件循环 Tick 中,Vue 刷新队列执行实际工作</li><li>异步策略:Vue 在内部对异步队列尝试使用原生的<code>Promise.then</code>、<code>MutationObserver</code>或<code>setImmediate</code>,如果执行环境都不支持,则会采用<code>setTimeout</code>代替</li></ul><p>响应式相关的,从<code>defineReactive</code>开始看</p><p><code>src/core/observer/index.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/2.png" alt="defineReactive"></p><p>当一个值发生变化的时候,会被拦截,然后开始设一个最新的值,设完之后让 dep 去通知更新</p><p><code>src/core/observer/dep.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/3.png" alt="notify"></p><p>subs 里存放的是和当前 key 相关的一堆 watcher</p><p><code>src/core/observer/watcher.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/4.png" alt="update"></p><p>进入 Watcher 实例</p><p>lazy 不会立即得到最新的值,而是等界面中用到相关的派生值时才会,比如计算属性</p><p>sync 设置同步,会立即执行更新,比如在用$watch 时,传入 immediate: true 时</p><p>queueWatcher 是 watcher 入队操作</p><p><code>src/core/observer/scheduler.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/5.png" alt="queueWatcher"></p><p>queueWatcher 会对 watcher 进行去重</p><p>nextTick:执行一个异步任务</p><p><code>nextTick(flushSchedulerQueue)</code>:这里就是异步执行 flushSchedulerQueue(刷新调度队列)</p><p>flushSchedulerQueue 会被放到微任务队列</p><p><code>src/core/util/next-tick.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/6.png" alt="nextTick"></p><p>这个<code>nextTick</code>方法,就是平时用的$nextTick</p><p><code>callbacks</code>是存放异步任务的数组(不是微任务队列),是将来<code>flushSchedulerQueue</code>要清空的任务的集合</p><p>对 cb 进行了一次封装之后放入<code>callbacks</code>,但并不会立即执行</p><p>通过<code>timerFunc()</code>尝试异步执行</p><p><code>timerFunc</code>是一个全局变量,根据当前平台进行判断,选择用何种方式异步执行<code>flushCallbacks</code></p><p><code>flushCallbacks</code>执行 callbacks 中的所有任务</p><p>流程:</p><ol><li>nextTick 把 flushSchedulerQueue 放入 callbacks</li><li>timerFunc() 用异步的方式执行 flushCallbacks()</li><li>flushCallbacks 执行了 flushSchedulerQueue</li><li>flushSchedulerQueue 清空存放所有 watcher 的 queue</li></ol><p>为什么要这么做?因为 callbacks 里面执行了很多组件的更新</p><p><code>src/core/observer/scheduler.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/7.png" alt="flushSchedulerQueue"></p><p>flushSchedulerQueue 对 queue 排序后遍历执行 watcher</p><p>真正执行(render watcher、 user watcher)更新操作的是<code>watcher.run()</code></p><p><code>src/core/observer/watcher.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/8.png" alt="run"></p><p>如果当前组件处于激活状态,就执行<code>get()</code></p><p>如果 watcher 是一个 render watcher,,那么<code>get()</code>就是组件更新函数</p><p>如果 watcher 是 user watcher,那么<code>get()</code>是计算当前选项最新值的函数</p><p>下面的<code>if(value !== this.value || isObject(value) || this.deep)</code>只有 user watcher(watch、$watch)才会执行</p><p><code>this.cb.call</code>是用户定义的回调函数</p><p>如果是 render watcher,只走到<code>get()</code></p><p><code>src/core/observer/watcher.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/9.png" alt="get"></p><p><code>get()</code>其实执行的是 getter,也就是组件更新函数</p><p><code>src/core/observer/watcher.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/10.png" alt="getter"></p><p><code>new Watcher(this, updateComponent)</code><br>在<code>new Watcher</code>的时候需要传递组件实例(this),组件更新函数(updateComponent)</p><p>如果 expOrFn 是函数就直接赋值给 getter,则他就是组件更新函数</p><p>Watcher 是什么时候建的?</p><p><code>src/platforms/web/runtime/index.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/11.png" alt="Vue.prototype.$mount"></p><p>在<code>$mount</code>中执行了<code>mountComponent</code></p><p><code>src/core/instance/lifecycle.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/12.png" alt="mountComponent"></p><p>挂载的时候只做两件事</p><ol><li>声明一个组件更新函数</li><li>创建组件对应的 Watcher 实例</li></ol><p>所以 Watcher 是在组件挂载的时候创建的</p><h4 id="组件更新流程:"><a href="#组件更新流程:" class="headerlink" title="组件更新流程:"></a>组件更新流程:</h4><ol><li>当组件内有一个数据发生变化的时候,会通知更新</li><li>通知更新之后。组件会被放到队列</li><li>放到队列后,会把刷新队列的任务以异步的方式执行</li><li>等到未来的某个时刻刷新队列</li><li>刷新队列的时候 watcher 真正执行的函数是 run()</li><li>run()真正执行的是 getter</li><li>getter 其实就是 updateComponent</li></ol><p>当组件内数据发生变化后,未来的某个时刻,updateComponent 会重新执行以下</p><p>组件是怎么更新的?</p><ol><li>执行 render 获得虚拟 dom</li><li>vdom 需要使用<code>_update()</code>转换为真实 dom</li></ol><p><code>src/core/instance/lifecycle.js</code><br><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/13.png" alt="_update"></p><p><code>_update</code>中判断是否是初始化,初始化的话就创建 vnode 为真实 dom,追加到 vm.$el 上,不是初始化的话就 diff</p><p><code>examples/test/03-timerFunc.html</code></p><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></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">"demo"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>异步更新<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"p1"</span>></span>{{foo}}<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">script</span>></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">const</span> app = <span class="keyword">new</span> Vue({</span></span><br><span class="line"><span class="javascript"> el: <span class="string">'#demo'</span>,</span></span><br><span class="line"> data: {</span><br><span class="line"><span class="javascript"> foo: <span class="string">'ready~~'</span>,</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mounted</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">1</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'1:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">2</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'2:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">3</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'3:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.$nextTick(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"> })</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>问: 此时 2 次 p1.innerHTML 的值是?<br>答:<code>ready~~,3</code></p><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></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">"demo"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>异步更新<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"p1"</span>></span>{{foo}}<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">script</span>></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">const</span> app = <span class="keyword">new</span> Vue({</span></span><br><span class="line"><span class="javascript"> el: <span class="string">'#demo'</span>,</span></span><br><span class="line"> data: {</span><br><span class="line"><span class="javascript"> foo: <span class="string">'ready~~'</span>,</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mounted</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">1</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'1:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.$nextTick(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">2</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'2:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">3</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'3:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>问: 此时 2 次 p1.innerHTML 的值是?<br>答:<code>ready~~,3</code></p><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></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">"demo"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>异步更新<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"p1"</span>></span>{{foo}}<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">script</span>></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">const</span> app = <span class="keyword">new</span> Vue({</span></span><br><span class="line"><span class="javascript"> el: <span class="string">'#demo'</span>,</span></span><br><span class="line"> data: {</span><br><span class="line"><span class="javascript"> foo: <span class="string">'ready~~'</span>,</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mounted</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.$nextTick(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">1</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'1:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">2</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'2:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">3</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'3:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>问: 此时 2 次 p1.innerHTML 的值是?<br>答:<code>ready~~,ready~~</code></p><p>原因:<br>$nextTick 会让 callbacks 数组中存放回调函数<br><code>this.foo = 1</code>会在 callbacks 中添加<code>flusScheduleQueue</code><br>前两种情况,foo 已经入队了,callbacks 队列类似<code>[flusScheduleQueue, nextTick.cb]</code>这样子<br>第三种情况时,foo 入队在 nextTick 后面,callbacks 中类似<code>[nextTick.cb, flusScheduleQueue]</code></p><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></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">"demo"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>异步更新<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"p1"</span>></span>{{foo}}<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">script</span>></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">const</span> app = <span class="keyword">new</span> Vue({</span></span><br><span class="line"><span class="javascript"> el: <span class="string">'#demo'</span>,</span></span><br><span class="line"> data: {</span><br><span class="line"><span class="javascript"> foo: <span class="string">'ready~~'</span>,</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">mounted</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">1</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'1:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">2</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'2:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.foo = <span class="number">3</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'3:'</span> + <span class="built_in">this</span>.foo)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"><span class="javascript"> <span class="built_in">Promise</span>.resolve().then(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'Promise p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"> })</span><br><span class="line"><span class="javascript"> <span class="built_in">this</span>.$nextTick(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(<span class="string">'nextTick p1.innerHTML:'</span> + p1.innerHTML)</span></span><br><span class="line"> })</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure><p>问:Promise 和 nextTick 谁先输出?<br>答:<code>nextTick</code></p><p>原因:<br><code>this.foo = 1</code>导致在 callbacks 中添加一个<code>flusScheduleQueue</code>,并将 callbacks 添加到微任务队列<br>Promise 执行时,将回调函数进入微任务队列<br>nextTick 执行时将回调函数进入 callbacks,此时 callbacks 类似<code>[flusScheduleQueue, nextTick.cb]</code><br>此时因为上面的微任务队列中是<code>[callbacks, Promise.then]</code>的样子<br>所以在执行 callbacks 时,会将 nextTick 一并执行后再执行 Promise.then</p><h3 id="虚拟-DOM"><a href="#虚拟-DOM" class="headerlink" title="虚拟 DOM"></a>虚拟 DOM</h3><p><strong>虚拟 DOM(Virtual DOM)是对 DOM 的 JS 抽象表示,它们是 JS 对象,能够描述 DOM 结构和关系。应用的各种状态变化会作用于虚拟 DOM,最终映射到 DOM 上。</strong></p><p>vue 中虚拟 dom 基于 <code>snabbdom</code> 实现,安装 snabbdom 并体验</p><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="meta-keyword">html</span>></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 class="tag"><<span class="name">head</span>></span><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">"app"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="comment"><!--安装并引入snabbdom--></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"https://cdn.bootcdn.net/ajax/libs/snabbdom/0.7.4/snabbdom.min.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></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">defineReactive</span>(<span class="params">obj, key, val</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">Object</span>.defineProperty(obj, key, {</span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">get</span>(<span class="params"></span>)</span> {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> val</span></span><br><span class="line"> },</span><br><span class="line"><span class="javascript"> <span class="function"><span class="title">set</span>(<span class="params">newVal</span>)</span> {</span></span><br><span class="line"> val = newVal</span><br><span class="line"><span class="javascript"> <span class="comment">// 通知更新</span></span></span><br><span class="line"> update()</span><br><span class="line"> },</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="comment">// 导入patch的工厂init,h是产生vnode的工厂</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> { init, h } = snabbdom</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 获取patch函数</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> patch = init([])</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 上次vnode,由patch()返回</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> vnode</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 更新函数,将数据操作转换为dom操作,返回新vnode</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">update</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"> if (!vnode) {</span><br><span class="line"><span class="javascript"> <span class="comment">// 初始化,没有上次vnode,传入宿主元素和vnode</span></span></span><br><span class="line"> vnode = patch(app, render())</span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 更新,传入新旧vnode对比并做更新</span></span></span><br><span class="line"> vnode = patch(vnode, render())</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="comment">// 渲染函数,返回vnode描述dom结构</span></span></span><br><span class="line"><span class="javascript"> <span class="function"><span class="keyword">function</span> <span class="title">render</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> h(<span class="string">'div'</span>, obj.foo)</span></span><br><span class="line"> }</span><br><span class="line"><span class="javascript"> <span class="comment">// 数据</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> obj = {}</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 定义响应式</span></span></span><br><span class="line"><span class="javascript"> defineReactive(obj, <span class="string">'foo'</span>, <span class="string">''</span>)</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 赋一个日期作为初始值</span></span></span><br><span class="line"><span class="javascript"> obj.foo = <span class="keyword">new</span> <span class="built_in">Date</span>().toLocaleTimeString()</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 定时改变数据,更新函数会重新执行</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">setInterval</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="javascript"> obj.foo = <span class="keyword">new</span> <span class="built_in">Date</span>().toLocaleTimeString()</span></span><br><span class="line"> }, 1000)</span><br><span class="line"> <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><h3 id="DIFF"><a href="#DIFF" class="headerlink" title="DIFF"></a>DIFF</h3><p>从<code>_update</code>看起</p><p><code>src/core/instance/lifecycle.js</code></p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/14.png" alt="_update"></p><p><code>__patch__</code>从何而来?</p><p><code>src/platforms/web/runtime/index.js</code></p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/15.png" alt="__patch__"></p><p><code>__patch</code>是在平台特有代码中指定的</p><p><code>src/core/vdom/patch.js</code></p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/16.png" alt="createPatchFunction"></p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/17.png" alt="初始化"></p><p><strong>diff 原则:深度优先,同层比较(为了降低时间复杂度)</strong></p><p>比较两个 VNode,包括三种类型操作:<em>属性更新_、_文本更新_、_子节点更新</em></p><p>具体规则如下:</p><ol><li>新老节点均有 children 子节点,则对子节点进行 diff 操作,调用 updateChildren</li><li>如果新节点有子节点而老节点没有子节点,先清空老节点的文本内容,然后为其新增子节点。</li><li>当新节点没有子节点而老节点有子节点的时候,则移除该节点的所有子节点。</li><li>当新老节点都无子节点的时候,只是文本的替换。</li></ol><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/18.png" alt="patchVnode"></p><h4 id="updateChildren-算法:"><a href="#updateChildren-算法:" class="headerlink" title="updateChildren 算法:"></a>updateChildren 算法:</h4><ol><li>新老 vnode 各创建一个首尾标记,向内双循环,直到<code>oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx</code></li><li>oldStartVnode 和 newStartVnodel 满足 sameVnode,就直接 patchVnode</li><li>oldStartVnode 和 newEndVnode 满足 sameVnode, patchVnode 同时将真实 dom 移到 oldEndVnode 后面</li><li>oldEndVnode 和 newStartVnode 满足 sameVnode,patchVnode 同时将真实 dom 移到 oldStartVnode 前面</li><li>如果都不满足,就在 oldVnode 中找和 newStartVnode 相同的节点,如果存在就 patchVnode 把真实 dom 添加到 oldStartIndex 前面</li><li>如果在 oldVnode 中找不到和 newStartVnode 相同的节点,就 调用 createElm,patchVnode 到 oldStartIndex 前面</li><li>如果 oldVnode 先遍历完,说明新的比老的多,就把剩下的 newVnode 插入真实 dom</li><li>如果 newVnode 先遍历完,说明老的比新的多,就把剩下的 oldVnode 对应的真实 dom 删除</li></ol><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/19.png" alt="updateChildren"></p><h4 id="key-的作用:"><a href="#key-的作用:" class="headerlink" title="key 的作用:"></a>key 的作用:</h4><p>先看<code>sameVnode</code>方法</p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/20.png" alt="sameVnode"></p><p>sameVnode 通过判断 tag(div、span……),注释,data,input 判断 type 等来判断是否相同</p><p>这是 isDef 方法<code>function isDef (v) { return v !== undefined && v !== null }</code></p><p>当不设置 key 的时候,key 就是<code>undefined</code>,<code>undefined === undefined</code>,在列表操作时,就会进行强制更新</p><p>例:数组<code>[1,2,3]</code>在 2 前面添加 4<br>有 key 时,<code>[1,2,3][1,4,2,3] => [2,3][4,2,3] => [2][4,2] => [][4]</code> 最后创建 4<br>无 key 时,<code>[1,2,3][1,4,2,3] => [2,3][4,2,3] => [3][2,3] => [][3]</code> 全都强制更新一遍,最后创建 3</p><p>所以平常使用时要加 key,且 key 要保持唯一,且不要用索引,不然会导致强制更新</p><h4 id="patch-函数是怎么获取的?"><a href="#patch-函数是怎么获取的?" class="headerlink" title="patch 函数是怎么获取的?"></a>patch 函数是怎么获取的?</h4><p><code>src/platforms/web/runtime/index.js</code></p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/21.png" alt="Vue.prototype.__patch__ "></p><p>传递平台特有的节点操作选项给工厂函数,返回 patch</p><p>这里 nodeOps 就是是 web 平台特有的 dom 操作</p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/22.png" alt="nodeOps"></p><p>modules 是把平台 modules 和 baseModules 做了一个拼接</p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/23.png" alt="platformModules"></p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/24.png" alt="baseModules"></p><p>平台属性相关的操作会以 module 的形式暴露,都会有钩子函数的名字,最终 patch 里执行的时候调用这个钩子对应的方法</p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/25.png" alt="钩子函数"></p><h4 id="节点属性如何更新?"><a href="#节点属性如何更新?" class="headerlink" title="节点属性如何更新?"></a>节点属性如何更新?</h4><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/27.png" alt="createPatchFunction"></p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/28.png" alt="patchVnode"></p><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/26.png" alt="cbs"></p><h3 id="思维导图"><a href="#思维导图" class="headerlink" title="思维导图"></a>思维导图</h3><p><img src="/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/29.png"></p><p><strong>本文完</strong></p>]]></content>
<summary type="html"><p>目录:</p>
<ul>
<li><a href="https://yongmaple.com/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li>
<li><a href="htt</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="手撕源码" scheme="https://yongmaple.com/tags/%E6%89%8B%E6%92%95%E6%BA%90%E7%A0%81/"/>
<category term="Vue" scheme="https://yongmaple.com/tags/Vue/"/>
</entry>
<entry>
<title>算法-两数之和</title>
<link href="https://yongmaple.com/2021/04/14/%E7%AE%97%E6%B3%95-%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8C/"/>
<id>https://yongmaple.com/2021/04/14/%E7%AE%97%E6%B3%95-%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8C/</id>
<published>2021-04-14T08:08:32.000Z</published>
<updated>2021-08-29T13:38:44.228Z</updated>
<content type="html"><![CDATA[<p>给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。</p><p>你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。</p><p>你可以按任意顺序返回答案。</p><h4 id="第一次解法"><a href="#第一次解法" class="headerlink" title="第一次解法"></a>第一次解法</h4><p>耗时:1008ms</p><p>内存:46.1MB</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> twoSum = <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> org = [...nums]</span><br><span class="line"> <span class="keyword">let</span> result = []</span><br><span class="line"> <span class="built_in">Object</span>.entries(nums).forEach(<span class="function">(<span class="params">v</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> [key, value] = v</span><br><span class="line"> <span class="built_in">Object</span>.entries(nums).forEach(<span class="function">(<span class="params">x</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> [key2, value2] = x</span><br><span class="line"> <span class="keyword">if</span> (key !== key2) {</span><br><span class="line"> <span class="keyword">if</span> (value + value2 === target) {</span><br><span class="line"> result.push(...[key, key2])</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> <span class="built_in">Array</span>.from(<span class="keyword">new</span> <span class="built_in">Set</span>(result))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="第二次解法"><a href="#第二次解法" class="headerlink" title="第二次解法"></a>第二次解法</h4><p>耗时:80ms</p><p>内存:37.9MB</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> twoSum = <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> hash = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < nums.length; i++) {</span><br><span class="line"> <span class="keyword">let</span> val = nums[i]</span><br><span class="line"> <span class="keyword">if</span> (hash.hasOwnProperty(target - val)) {</span><br><span class="line"> <span class="keyword">return</span> [hash[target - val], i]</span><br><span class="line"> }</span><br><span class="line"> hash[val] = i</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。</p>
<p>你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。</p>
<p>你可以按任意顺序返回</summary>
<category term="算法" scheme="https://yongmaple.com/tags/%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>Vue2源码解析</title>
<link href="https://yongmaple.com/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
<id>https://yongmaple.com/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</id>
<published>2021-04-13T17:09:57.000Z</published>
<updated>2021-08-29T13:38:44.144Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="https://yongmaple.com/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li><li><a href="https://yongmaple.com/2021/04/15/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%BA%8C/">Vue2 源码解析二</a></li><li><a href="https://yongmaple.com/2021/04/21/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90%E4%B8%89/">Vue2 源码解析三</a></li></ul><p>本文项目地址:<a href="https://github.com/YongMaple/vue">https://github.com/YongMaple/vue</a> 内含测试用代码<code>/examples/test/</code></p><h3 id="获取-Vue"><a href="#获取-Vue" class="headerlink" title="获取 Vue"></a>获取 Vue</h3><p>项目地址:<a href="https://github.com/vuejs/vue">https://github.com/vuejs/vue</a></p><p>当前版本号:2.6.12</p><h3 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h3><figure class="highlight bash"><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">npm i</span><br><span class="line">npm i roll-up -g</span><br></pre></td></tr></table></figure><p>使用<code>npm run dev</code>就会在<code>dist</code>目录下生成编译后的<code>vue.js</code></p><h3 id="文件结构"><a href="#文件结构" class="headerlink" title="文件结构"></a>文件结构</h3><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── <span class="keyword">BACKERS.md</span></span><br><span class="line"><span class="keyword">├── </span>LICENSE</span><br><span class="line">├── README.md</span><br><span class="line">├── <span class="keyword">benchmarks</span></span><br><span class="line"><span class="keyword">├── </span><span class="keyword">dist </span> 发布目录</span><br><span class="line">├── examples 范例,里面有测试代码</span><br><span class="line">├── flow 针对flow的类型声明(类似ts)</span><br><span class="line">├── package.<span class="keyword">json</span></span><br><span class="line"><span class="keyword">├── </span>packages 核心代码之外的独立库</span><br><span class="line">├── <span class="keyword">scripts </span> 构建脚本</span><br><span class="line">├── src 源码</span><br><span class="line">│ ├── compiler 编译器相关(渲染函数、SFC的编译器、dom编译器)</span><br><span class="line">│ ├── core 核心代码</span><br><span class="line">│ │ ├── components 通用组件如keep-alive</span><br><span class="line">│ │ ├── <span class="built_in">config</span>.<span class="keyword">js</span></span><br><span class="line"><span class="keyword">│ </span> │ ├── global-api 全局API</span><br><span class="line">│ │ ├── index.<span class="keyword">js</span></span><br><span class="line"><span class="keyword">│ </span> │ ├── <span class="keyword">instance </span> 构造函数等</span><br><span class="line">│ │ ├── observer 响应式相关</span><br><span class="line">│ │ ├── util</span><br><span class="line">│ │ └── vdom 虚拟DOM相关</span><br><span class="line">│ ├── platforms 平台</span><br><span class="line">│ │ ├── web</span><br><span class="line">│ │ └── weex 基本凉凉了</span><br><span class="line">│ ├── server</span><br><span class="line">│ ├── sfc</span><br><span class="line">│ └── <span class="keyword">shared</span></span><br><span class="line"><span class="keyword">├── </span>types ts类型声明</span><br><span class="line">└── yarn.lock</span><br></pre></td></tr></table></figure><p>需要源码映射,所以在 package.json 中添加<code>--sourcemap</code></p><p><code>"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",</code></p><p>术语解释:</p><ul><li>runtime: 仅包含运行时,不包含编译器</li><li>common: cjs 规范,用于 webpack1</li><li>esm: ES 模块,用于 webpack2+</li><li>umd: universal module definition,兼容 cjs 和 amd,常见直接用在浏览器</li></ul><p>例:</p><ul><li><code>/dist/vue.common.js</code> 中间加 common 的,给服务端使用的,在 nodejs 环境执行的</li><li><code>/dist/vue.esm.browser.js</code> esm,给打包工具使用的,如 webpack。</li><li>browser,给支持 type=module 这种模块化方式导入的浏览器</li><li><code>/dist/vue.js</code> global,同时兼容 amd 和 cjs(commonjs)的格式。umd 的打包方式</li><li><code>/dist/vue.runtime.js</code> runtime,运行时,不含编译器,template 中加入字符串模板这类操作会报错</li></ul><h3 id="src-platforms-web-entry-runtime-with-compiler-js"><a href="#src-platforms-web-entry-runtime-with-compiler-js" class="headerlink" title="src/platforms/web/entry-runtime-with-compiler.js"></a><code>src/platforms/web/entry-runtime-with-compiler.js</code></h3><p>从 package.json <code>dev</code>中找到<code>/scripts/config.js</code></p><p><code>rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev</code>命令中传入了环境变量<code>web-full-dev</code></p><p>在<code>/scripts/config.js</code>中查找,可以在配置中找到入口文件<code>web/entry-runtime-with-compiler.js</code></p><p>文件地址前有个 resolve 方法</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">const</span> resolve = <span class="function">(<span class="params">p</span>) =></span> {</span><br><span class="line"> <span class="comment">// 拆分/</span></span><br><span class="line"> <span class="comment">// web/entry-runtime-with-compiler.js</span></span><br><span class="line"> <span class="keyword">const</span> base = p.split(<span class="string">'/'</span>)[<span class="number">0</span>] <span class="comment">// web</span></span><br><span class="line"> <span class="keyword">if</span> (aliases[base]) {</span><br><span class="line"> <span class="keyword">return</span> path.resolve(aliases[base], p.slice(base.length + <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> path.resolve(__dirname, <span class="string">'../'</span>, p)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>以下是<code>aliases</code>的内容</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> resolve = <span class="function">(<span class="params">p</span>) =></span> path.resolve(__dirname, <span class="string">'../'</span>, p)</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> vue: resolve(<span class="string">'src/platforms/web/entry-runtime-with-compiler'</span>),</span><br><span class="line"> compiler: resolve(<span class="string">'src/compiler'</span>),</span><br><span class="line"> core: resolve(<span class="string">'src/core'</span>),</span><br><span class="line"> shared: resolve(<span class="string">'src/shared'</span>),</span><br><span class="line"> web: resolve(<span class="string">'src/platforms/web'</span>),</span><br><span class="line"> weex: resolve(<span class="string">'src/platforms/weex'</span>),</span><br><span class="line"> server: resolve(<span class="string">'src/server'</span>),</span><br><span class="line"> sfc: resolve(<span class="string">'src/sfc'</span>),</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>现在找到了入口文件的位置<code>src/platforms/web/entry-runtime-with-compiler.js</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> mount = Vue.prototype.$mount</span><br><span class="line">Vue.prototype.$mount = <span class="function"><span class="keyword">function</span> (<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params"> ...</span></span></span><br><span class="line"><span class="function"><span class="params">}</span></span></span><br></pre></td></tr></table></figure><p>源码中这段是为了扩展$mount,为了解析 el、template 选项</p><p>问题 1:为什么可以不用写<code>$mount</code></p><p>答:如果设置了 el,挂载的宿主就指定为 el</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">const</span> app = <span class="keyword">new</span> Vue({</span><br><span class="line"> el: <span class="string">'#app'</span>,</span><br><span class="line"> <span class="comment">// template: '<div>template</div></span></span><br><span class="line"> template: <span class="string">'#app'</span>,</span><br><span class="line"> <span class="function"><span class="title">render</span>(<span class="params">h</span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> h(<span class="string">'div'</span>, <span class="string">'render'</span>)</span><br><span class="line"> },</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>问题 2:el、template、render 同时出现,哪个优先级高</p><p>答:render > template > el</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/1.png" alt="注意看注释"></p><h3 id="src-platforms-web-runtime-index-js"><a href="#src-platforms-web-runtime-index-js" class="headerlink" title="src/platforms/web/runtime/index.js"></a><code>src/platforms/web/runtime/index.js</code></h3><p>找到当前文件中的 Vue 引入<code>import Vue from './runtime/index'</code></p><p>进入这个文件看一下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// install platform patch function</span></span><br><span class="line"><span class="comment">// 安装平台特有的补丁函数,未来用于组件更新</span></span><br><span class="line">Vue.prototype.__patch__ = inBrowser ? patch : noop</span><br><span class="line"></span><br><span class="line"><span class="comment">// public mount method</span></span><br><span class="line"><span class="comment">// 实现了$mount</span></span><br><span class="line">Vue.prototype.$mount = <span class="function"><span class="keyword">function</span> (<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params"> el?: string | Element,</span></span></span><br><span class="line"><span class="function"><span class="params"> hydrating?: boolean</span></span></span><br><span class="line"><span class="function"><span class="params"></span>): <span class="title">Component</span> </span>{</span><br><span class="line"> el = el && inBrowser ? query(el) : <span class="literal">undefined</span></span><br><span class="line"> <span class="comment">// 挂载组件:把当前组件(this)挂载到el上</span></span><br><span class="line"> <span class="comment">// 流程: mountComponent 执行this的render, 得到vnode, _update(vnode)转换成真实dom, _update内部调用的是__patch__</span></span><br><span class="line"> <span class="keyword">return</span> mountComponent(<span class="built_in">this</span>, el, hydrating)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><em>Vue 在执行挂载时:执行渲染函数得到虚拟 dom,再执行更新函数,将其转换为真实 dom</em></p><p>继续找 Vue,当前文件的引入为<code>import Vue from 'core/index'</code></p><p>此处 core 是<code>src/core</code>的别名</p><h3 id="src-core-index-js"><a href="#src-core-index-js" class="headerlink" title="src/core/index.js"></a><code>src/core/index.js</code></h3><p>主要做了初始化全局 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 初始化全局API</span></span><br><span class="line"><span class="comment">// Vue.use directive components mixin 等</span></span><br><span class="line">initGlobalAPI(Vue)</span><br></pre></td></tr></table></figure><p>继续找 Vue,当前文件的引入为<code>import Vue from './instance/index'</code></p><h3 id="src-core-instance-index-js"><a href="#src-core-instance-index-js" class="headerlink" title="src/core/instance/index.js"></a><code>src/core/instance/index.js</code></h3><p>这个文件中</p><ul><li>声明了 Vue 的构造函数</li><li>声明了 Vue 的各种实例方法($set、$watch 等等)</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><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 声明Vue的构造函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Vue</span>(<span class="params">options</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span> && !(<span class="built_in">this</span> <span class="keyword">instanceof</span> Vue)) {</span><br><span class="line"> warn(<span class="string">'Vue is a constructor and should be called with the `new` keyword'</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">this</span>._init(options)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明Vue各种实例方法</span></span><br><span class="line">initMixin(Vue)</span><br><span class="line">stateMixin(Vue)</span><br><span class="line">eventsMixin(Vue)</span><br><span class="line">lifecycleMixin(Vue)</span><br><span class="line">renderMixin(Vue)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Vue</span><br></pre></td></tr></table></figure><p>这里<code>_init</code>从何而来,是在<code>initMixin()</code>中</p><h3 id="src-core-instance-init-js"><a href="#src-core-instance-init-js" class="headerlink" title="src/core/instance/init.js"></a><code>src/core/instance/init.js</code></h3><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/2.png" alt="注意注释"></p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/5.png" alt="initLifecycle"></p><p>内部就是声明了各种和他有关系的组件,父、祖、子等</p><p>就是实例属性的初始化</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/3.png" alt="initEvents"></p><p>在使用自定义事件时,类似这种写法<code><Child @my-click="onClick"></Child></code></p><p>回调函数(onClick)在父组件中声明的,所以用在 initEvents 中使用<code>_parentListeners</code></p><p><code>updateComponentListeners</code>是事件的监听</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></pre></td><td class="code"><pre><span class="line">child.$emit(<span class="string">'my-click'</span>)</span><br><span class="line">child.$on(<span class="string">'my-click'</span>, listeners)</span><br></pre></td></tr></table></figure><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/4.png" alt="initRender"></p><p>在 render 中处理<code>$slots</code>和<code>$scopedSlots</code>这个比较好理解,在渲染前肯定是要把内部插槽先解析</p><p><code>$_c</code>和<code>$createElement</code>就是 render(h)中的那个 h,可以得到虚拟 dom</p><p><code>$_c</code>给编译器生成的渲染函数去使用</p><p><code>$createElement</code>给用户生成的渲染函数去使用</p><p>问:new Vue 的时候都发生了什么?</p><p>答:首先构造函数的初始化会得到一个 Vue 的实例,我们给他传入 options,我们会得到一个组件实例,在内部会做根实例的初始化</p><ol><li>初始化时会做:当前实例的关键属性初始化,例如$parent,$root,$slots,$scopeSlots 等等</li><li>同时对当前自定义组件的自定义事件的监听</li><li>派发一些生命周期钩子,beforeCreate、created</li><li>在这两个钩子中间,对组件的状态进行初始化,比如 data/props/methods/computed/watch,对他们进行数据响应式处理</li></ol><h3 id="数据响应式"><a href="#数据响应式" class="headerlink" title="数据响应式"></a>数据响应式</h3><p>可以先看下之前的这篇文章进行大致的了解</p><p><a href="https://yongmaple.com/2021/04/07/%E6%89%8B%E5%86%99Vue/">https://yongmaple.com/2021/04/07/手写 Vue/</a></p><p>开始研究<code>initState</code></p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/6.png" alt="initState"><br>可以看到 initState 里面,对 props、methods、data、computed、watch 进行了初始化</p><p>问:props、methods、data 中如果有重名的属性,优先谁?</p><p>答:props > methods > data。从上往下处理,在处理时如果发现有重名的,就会报错</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/7.png" alt="initData"></p><p>通常 data 都是 function,只有在根组件的时候可以是对象</p><p>问:为什么这里的 data 可以是对象?为什么 data 一般都是个函数,需要 return 出去,不能直接写对象?</p><ul><li>组件复用时所有组件实例都会共享 data,如果 data 是对象的话,就会造成一个组件修改 data 以后会影响到其他所有组件,所以需要将 data 写成函数,每次用到就调用一次函数获得新的数据。</li><li>当我们使用 new Vue() 的方式的时候,无论我们将 data 设置为对象还是函数都是可以的,因为 new Vue() 的方式是生成一个根组件,该组件不会复用,也就不存在共享 data 的情况了</li></ul><p>这里进入<code>observe</code>方法(<code>src/core/observer/index.js</code>)</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/8.png" alt="observe"></p><p>可以知道,在 observe 时,每个对象会创建一个 Observer</p><p>先看测试代码<code>examples/test/02-1-reactive.html</code>中的问题</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/9.png" alt="02-1-reactive.html"></p><p>再看下<code>src/core/observer/index.js</code></p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/10.png" alt="Observer"></p><p>再看上面的问题,这个例子中有几个 Dep,现在就知道应该是 4 个了</p><p>每个对象创建一个 Dep,每个属性又会创建一个子 Dep,所以这里是 4 个 Dep</p><p>一个组件一个 Watcher,这个例子中只有一个 new Vue(),所以这里是 1 个 Watcher</p><p>进入 Observer 中的 this.walk</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/11.png" alt="walk"></p><p>这里 walk 遍历了对象的所有 key,进行了一个响应式的处理</p><p>进入 defineReactive</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/12.png" alt="defineReactive"></p><p>这里每个 key 都 new Dep()</p><p>还可以看到<code>let childOb = !shallow && observe(val)</code></p><p>这里又对属性做了一次 observe</p><p>如果这里 childOb 存在,就说明子 Ob 内部的 dep 和当前组件的 watcher 建立了依赖关系</p><p>Dep 与 Watcher 的关系</p><ul><li>一个组件内部只有一个 Watcher</li><li>一个组件内部有多个 Dep</li><li>组件内部除了 render watcher,可能还会有 user watcher,用户会自定义($watch、watch:{})</li><li>所以他们之间是多对多的关系</li></ul><p>先看下 depend</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/13.png" alt="depend"></p><p>这里面 Dep.target 就是 WatcheraddDep</p><p>再看下 Watcher 的 addDep</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/14.png" alt="addDep"></p><p>这里面把 watcher 和 dep 相互建立了关系</p><ul><li><p>建立 Dep 与 Watcher 的关系是为了通知更新,这个很好理解</p></li><li><p>建立 Watcher 与 Dep 的关系是为了清除 watcher 时使用</p></li></ul><p>如何清除 watcher?</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="keyword">const</span> unWatch = app.$watch(<span class="string">'text'</span>, <span class="function">(<span class="params">newVal, oldVal</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${newVal}</span> : <span class="subst">${oldVal}</span>`</span>)</span><br><span class="line">})</span><br><span class="line"><span class="comment">// 手动注销watch</span></span><br><span class="line">unWatch()</span><br></pre></td></tr></table></figure><h3 id="数组响应式"><a href="#数组响应式" class="headerlink" title="数组响应式"></a>数组响应式</h3><p>数组和对象不同,操作数组时使用 7 个方法(数组变更方法),没办法得知数据变化,vue 中采取的策略是拦截这些方法并通知 dep</p><p>先看对数组的拦截和通知 <code>src/core/observer/array.js</code></p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/15.png" alt="array.js"></p><p>再找<code>arrayMethods</code>在哪使用的</p><p>Observer 中有对数组和对象分别做处理</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">export</span> <span class="class"><span class="keyword">class</span> <span class="title">Observer</span> </span>{</span><br><span class="line"> ...</span><br><span class="line"> <span class="comment">// 根据Object或者Array做不同的操作</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(value)) {</span><br><span class="line"> <span class="comment">// 判断是否有原型</span></span><br><span class="line"> <span class="keyword">if</span> (hasProto) {</span><br><span class="line"> protoAugment(value, arrayMethods)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// IE等老版本浏览器</span></span><br><span class="line"> copyAugment(value, arrayMethods, arrayKeys)</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">this</span>.observeArray(value)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">this</span>.walk(value)</span><br><span class="line"> }</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">protoAugment</span>(<span class="params">target, src: <span class="built_in">Object</span></span>) </span>{</span><br><span class="line"> <span class="comment">/* eslint-disable no-proto */</span></span><br><span class="line"> <span class="comment">// 覆盖当前数组实例的原型</span></span><br><span class="line"> target.__proto__ = src</span><br><span class="line"> <span class="comment">/* eslint-enable no-proto */</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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">copyAugment</span>(<span class="params">target: <span class="built_in">Object</span>, src: <span class="built_in">Object</span>, keys: <span class="built_in">Array</span><string></span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, l = keys.length; i < l; i++) {</span><br><span class="line"> <span class="keyword">const</span> key = keys[i]</span><br><span class="line"> def(target, key, src[key])</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="set,del,watch"><a href="#set,del,watch" class="headerlink" title="set,del,watch"></a>set,del,watch</h3><p>set 所在位置<code>src/core/observer/index.js</code></p><p><em>图中删了两个:any,不然颜色有问题……</em></p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/17.png" alt="set"></p><p>del 就在 set 的下面,比较简单,只是删除后通知了一下</p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/18.png" alt="del"></p><p>watch 所在位置<code>src/core/instance/state.js</code></p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/19.png" alt="$watch"></p><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/20.png" alt="createWatcher"></p><h3 id="思维导图"><a href="#思维导图" class="headerlink" title="思维导图"></a>思维导图</h3><p><img src="/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/16.png"></p><p><strong>本文完</strong></p>]]></content>
<summary type="html"><p>目录:</p>
<ul>
<li><a href="https://yongmaple.com/2021/04/13/Vue2%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/">Vue2 源码解析</a></li>
<li><a href="htt</summary>
<category term="前端" scheme="https://yongmaple.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="手撕源码" scheme="https://yongmaple.com/tags/%E6%89%8B%E6%92%95%E6%BA%90%E7%A0%81/"/>
<category term="Vue" scheme="https://yongmaple.com/tags/Vue/"/>
</entry>
</feed>