-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
916 lines (682 loc) · 154 KB
/
index.html
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
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.0">
<script data-ad-client="ca-pub-6412444017075171" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="/images/avatar.jpeg">
<link rel="icon" type="image/png" sizes="32x32" href="/images/avatar.jpeg">
<link rel="icon" type="image/png" sizes="16x16" href="/images/avatar.jpeg">
<link rel="mask-icon" href="/images/avatar.jpeg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/font-awesome@4/css/font-awesome.min.css">
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {
hostname: new URL('https://micsay.com').hostname,
root: '/',
scheme: 'Pisces',
version: '7.7.0',
exturl: false,
sidebar: {"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},
copycode: {"enable":false,"show_result":false,"style":null},
back2top: {"enable":true,"sidebar":false,"scrollpercent":false},
bookmark: {"enable":true,"color":"#222","save":"auto"},
fancybox: false,
mediumzoom: false,
lazyload: false,
pangu: false,
comments: {"style":"tabs","active":"gitalk","storage":true,"lazyload":false,"nav":{"gitalk":{"order":-2}},"activeClass":"gitalk"},
algolia: {
appID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
},
localsearch: {"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},
path: 'search.xml',
motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}
};
</script>
<meta property="og:type" content="website">
<meta property="og:title" content="细水会流长">
<meta property="og:url" content="https://micsay.com/index.html">
<meta property="og:site_name" content="细水会流长">
<meta property="article:author" content="崔">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="https://micsay.com/">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome: true,
isPost: false
};
</script>
<title>细水会流长</title>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-161259091-1"></script>
<script>
if (CONFIG.hostname === location.hostname) {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-161259091-1');
}
</script>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
<link rel="alternate" href="/atom.xml" title="细水会流长" type="application/atom+xml">
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-meta">
<div>
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">细水会流长</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<h1 class="site-subtitle" itemprop="description">目前就职于新浪微博</h1>
</div>
<div class="site-nav-toggle">
<div class="toggle" aria-label="切换导航栏">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="fa fa-fw fa-home"></i>首页</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags/" rel="section"><i class="fa fa-fw fa-tags"></i>标签</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section"><i class="fa fa-fw fa-archive"></i>归档</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/categories/" rel="section"><i class="fa fa-fw fa-th"></i>分类</a>
</li>
<li class="menu-item menu-item-search">
<a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
</a>
</li>
</ul>
</nav>
<div class="site-search">
<div class="popup search-popup">
<div class="search-header">
<span class="search-icon">
<i class="fa fa-search"></i>
</span>
<div class="search-input-container">
<input autocomplete="off" autocorrect="off" autocapitalize="none"
placeholder="搜索..." spellcheck="false"
type="text" id="search-input">
</div>
<span class="popup-btn-close">
<i class="fa fa-times-circle"></i>
</span>
</div>
<div id="search-result"></div>
</div>
<div class="search-pop-overlay"></div>
</div>
</div>
</header>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<a role="button" class="book-mark-link book-mark-link-fixed"></a>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content">
<div class="posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
<link itemprop="mainEntityOfPage" href="https://micsay.com/2020/10/08/mytv-assessiblity/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpeg">
<meta itemprop="name" content="崔">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="细水会流长">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2020/10/08/mytv-assessiblity/" class="post-title-link" itemprop="url">智能电视开机直接看电视思路</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2020-10-08 21:37:21 / 修改时间:22:18:34" itemprop="dateCreated datePublished" datetime="2020-10-08T21:37:21+08:00">2020-10-08</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E6%96%B9%E6%B3%95/" itemprop="url" rel="index">
<span itemprop="name">方法</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<blockquote>
<p>特别声明:本文未经许可禁止转载</p>
</blockquote>
<h3 id="提出问题"><a href="#提出问题" class="headerlink" title="提出问题"></a>提出问题</h3><p>国庆逢中秋长假,回家休假,我妈吐槽家里小米智能电视,该智能的地方不智能(比如开机直接看电视),不该智能的地方超智能(比如随时随地提醒你开通小米影视会员、屏保结束就显示超亮眼的广告,做广告比做产品做的都用心),真的是,需要它的地方它不行,不需要的地方比谁都聪明。我也怀疑,我花钱买了一个小米电视,买来之后,这电视仍旧不是我家的,而是小米的,小米想放广告就放广告,想看电视直播?没门。白马非马,智能电视非电视。</p>
<h3 id="问题调查以及解决"><a href="#问题调查以及解决" class="headerlink" title="问题调查以及解决"></a>问题调查以及解决</h3><h4 id="问题一:看不了电视直播"><a href="#问题一:看不了电视直播" class="headerlink" title="问题一:看不了电视直播"></a>问题一:看不了电视直播</h4><p>小米电视应用商店里面,是搜不到电视直播app的,经过一番搜索,使用U盘,安装成功,问题解决。</p>
<h4 id="问题二:开机不能直接进电视直播"><a href="#问题二:开机不能直接进电视直播" class="headerlink" title="问题二:开机不能直接进电视直播"></a>问题二:开机不能直接进电视直播</h4><p>对于年轻人来说,开机显示小米桌面,可能无所谓。但是对于年纪稍大的人来说,就没有那么方便了。年纪稍大的人,本来能用手机就不错了,现在想看电视,都还要使用遥控器左右选择,进入“我的”,进入”我的应用“,选择电视直播的app,偶尔还要将烦人的购买影视会员弹窗取消掉,实在麻烦。</p>
<p>我买的小米电视(由于它老显示广告,所以我觉得目前它不是我家的电视,所以称它为我买的小米电视),内置的系统是Android 6.0,如果想Root后让电视直播app支持自启动,会比较麻烦,并且Root后,系统可能会不稳定,不适合我妈这种小白。经过一番调研,我放弃了Root的想法。</p>
<p>小米电视只允许它内置的应用自启动,其他应用不行,自启动的应用有:小米桌面,小爱同学(遥控器语音控制)等等。小米桌面是Android的主屏幕,小爱同学是使用的Android无障碍。那么,想让电视直播app自启动,就有了一点转机。</p>
<p>方案一:替换小米桌面。将小米桌面直接替换成其他的支持自定义的TV桌面,减少进入电视直播app的操作步骤。经过调研,此方案可行,也有人实施了,但是是不可逆的,因为小米电视桌面被设死了,无法通过系统设置更改主屏幕,需要把小米电视桌面直接替换掉,方案欠佳。</p>
<p>方案二:仿照小爱同学,走无障碍的路。自己写一个无障碍app(我命名为我家辅助)并启用,则小米电视开机时,我家辅助启动,再在我家辅助中,启动电视直播app的Activity,则实现了小米电视启动直接进入电视直播,方案完美。</p>
<h3 id="问题延伸"><a href="#问题延伸" class="headerlink" title="问题延伸"></a>问题延伸</h3><p>使用Android的无障碍,走的是Android官方的路,不怕小米电视封杀。用无障碍实现这个开机进入电视直播的功能,实在是小事一桩。无障碍如果继续扩展,则还可以用于去广告,比如干掉烦人的小米影视会员弹窗。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
<link itemprop="mainEntityOfPage" href="https://micsay.com/2020/08/01/get-start-on-vue/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpeg">
<meta itemprop="name" content="崔">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="细水会流长">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2020/08/01/get-start-on-vue/" class="post-title-link" itemprop="url">Vue源码解析</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2020-08-01 12:48:02" itemprop="dateCreated datePublished" datetime="2020-08-01T12:48:02+08:00">2020-08-01</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-check-o"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2020-08-08 17:38:25" itemprop="dateModified" datetime="2020-08-08T17:38:25+08:00">2020-08-08</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%89%8D%E7%AB%AF/" itemprop="url" rel="index">
<span itemprop="name">前端</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<blockquote>
<p>特别声明:本文未经许可禁止转载</p>
</blockquote>
<h3 id="开头"><a href="#开头" class="headerlink" title="开头"></a>开头</h3><ul>
<li>不能免俗,我也要做Vue源码解析了</li>
<li>本文基于<code>Vue 2.5.18-beta.0</code>版本进行分析</li>
<li>源码讲解的第一行附了源码路径,可供查看,我会在我认为重要的地方加注释</li>
<li>如果有疑问,可以留言或者关注我公众号并发送消息</li>
<li>本文通俗易懂,不人云亦云,应该都能看得懂</li>
</ul>
<h3 id="Vue-Compiler:编译Vue模版"><a href="#Vue-Compiler:编译Vue模版" class="headerlink" title="Vue Compiler:编译Vue模版"></a>Vue Compiler:编译Vue模版</h3><ul>
<li>先来看一下,开发者开发时的Vue代码结构<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <div></span><br><span class="line"> <span>Hello World</span></span><br><span class="line"> </div></span><br><span class="line"></template></span><br></pre></td></tr></table></figure></li>
<li>以上代码,经过Vue compiler处理之后,template会变成<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">_c("div", [_c("span", [_vm._v("Hello World")])])</span><br></pre></td></tr></table></figure></li>
<li>其中,_c就是createElement,定义在<figure class="highlight plain"><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/src/core/instance/render.js</span><br><span class="line">vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)</span><br></pre></td></tr></table></figure></li>
<li>看一下Vue compiler入口,可猜出该部分的功能<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">// vue/src/compiler/index.js</span><br><span class="line">export const createCompiler = createCompilerCreator(function baseCompile (</span><br><span class="line"> template: string,</span><br><span class="line"> options: CompilerOptions</span><br><span class="line">): CompiledResult {</span><br><span class="line"> const ast = parse(template.trim(), options)</span><br><span class="line"> if (options.optimize !== false) {</span><br><span class="line"> optimize(ast, options)</span><br><span class="line"> }</span><br><span class="line"> const code = generate(ast, options)</span><br><span class="line"> return {</span><br><span class="line"> ast,</span><br><span class="line"> render: code.render,</span><br><span class="line"> staticRenderFns: code.staticRenderFns</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li>
<li>输入是模版string,输出是render函数,而这个render函数,将会被Vue-loader使用,将其声明到options中,后面Vue.prototype._render()函数中使用到到options.render(xx)函数,就是由此而来。另外Vue compiler还对某些Vue特性做了处理<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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></pre></td><td class="code"><pre><span class="line">Vue.prototype._render = function (): VNode {</span><br><span class="line"> const vm: Component = this</span><br><span class="line"> const { render, _parentVnode } = vm.$options // 1. 该render函数就是Vue compiler编译输出的</span><br><span class="line"></span><br><span class="line"> if (_parentVnode) {</span><br><span class="line"> vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // set parent vnode. this allows render functions to have access</span><br><span class="line"> // to the data on the placeholder node.</span><br><span class="line"> vm.$vnode = _parentVnode</span><br><span class="line"> // render self</span><br><span class="line"> let vnode</span><br><span class="line"> try {</span><br><span class="line"> // 2、生成vnode,供后面做VDOM DIFF时使用</span><br><span class="line"> vnode = render.call(vm._renderProxy, vm.$createElement)</span><br><span class="line"> } catch (e) {</span><br><span class="line"></span><br><span class="line"> handleError(e, vm, `render`)</span><br><span class="line"> // return error render result,</span><br><span class="line"> // or previous vnode to prevent render error causing blank component</span><br><span class="line"> /* istanbul ignore else */</span><br><span class="line"> if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {</span><br><span class="line"> try {</span><br><span class="line"> vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)</span><br><span class="line"> } catch (e) {</span><br><span class="line"> handleError(e, vm, `renderError`)</span><br><span class="line"> vnode = vm._vnode</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> vnode = vm._vnode</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> // return empty vnode in case the render function errored out</span><br><span class="line"> if (!(vnode instanceof VNode)) {</span><br><span class="line"> if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {</span><br><span class="line"> warn(</span><br><span class="line"> 'Multiple root nodes returned from render function. Render function ' +</span><br><span class="line"> 'should return a single root node.',</span><br><span class="line"> vm</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> vnode = createEmptyVNode()</span><br><span class="line"> }</span><br><span class="line"> // set parent</span><br><span class="line"> vnode.parent = _parentVnode</span><br><span class="line"> return vnode</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="Vue-Instance第一次渲染(总计有12个重要注释)"><a href="#Vue-Instance第一次渲染(总计有12个重要注释)" class="headerlink" title="Vue Instance第一次渲染(总计有12个重要注释)"></a>Vue Instance第一次渲染(总计有12个重要注释)</h3><ul>
<li>一切要从<code>new Vue(options)</code>开始<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">var app = new Vue({</span><br><span class="line"> el: '#app',</span><br><span class="line"> data: {</span><br><span class="line"> message: 'Hello Vue!'</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li>
<li>其实在<code>new Vue(options)</code>之前,需要先考虑<code>Vue Constructor</code>的初始化<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">// vue/src/core/instance/index.js</span><br><span class="line">function Vue (options) {</span><br><span class="line"> if (process.env.NODE_ENV !== 'production' &&</span><br><span class="line"> !(this instanceof Vue)</span><br><span class="line"> ) {</span><br><span class="line"> warn('Vue is a constructor and should be called with the `new` keyword')</span><br><span class="line"> }</span><br><span class="line"> this._init(options) // 1. new Vue(options)时执行</span><br><span class="line"> // _init方法即Vue.prototype._init</span><br><span class="line">}</span><br><span class="line">// 2. 以下方法调用,就是在初始化Vue Constructor,从方法名不难猜出其功能</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">export default Vue</span><br></pre></td></tr></table></figure></li>
<li>在<code>this._init(options)</code>方法中,初始化<code>Vue Instance</code>,我们来看看<code>_init</code>方法的定义<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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></pre></td><td class="code"><pre><span class="line">// vue/src/core/instance/init.js</span><br><span class="line"> Vue.prototype._init = function (options?: Object) { </span><br><span class="line"> // 3. 你可能奇怪javascript怎么会有类型声明,这是prop-types在编译时用到的,</span><br><span class="line"> // 编译结束之后,就是纯碎的javascript代码了</span><br><span class="line"> const vm: Component = this</span><br><span class="line"> // a uid</span><br><span class="line"> vm._uid = uid++</span><br><span class="line"></span><br><span class="line"> let startTag, endTag</span><br><span class="line"> /* istanbul ignore if */</span><br><span class="line"> if (process.env.NODE_ENV !== 'production' && config.performance && mark) {</span><br><span class="line"> startTag = `vue-perf-start:${vm._uid}`</span><br><span class="line"> endTag = `vue-perf-end:${vm._uid}`</span><br><span class="line"> mark(startTag)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // a flag to avoid this being observed</span><br><span class="line"> vm._isVue = true</span><br><span class="line"> // merge options</span><br><span class="line"> if (options && options._isComponent) {</span><br><span class="line"> // optimize internal component instantiation</span><br><span class="line"> // since dynamic options merging is pretty slow, and none of the</span><br><span class="line"> // internal component options needs special treatment.</span><br><span class="line"> initInternalComponent(vm, options)</span><br><span class="line"> } else {</span><br><span class="line"> vm.$options = mergeOptions(</span><br><span class="line"> resolveConstructorOptions(vm.constructor),</span><br><span class="line"> options || {},</span><br><span class="line"> vm</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> /* istanbul ignore else */</span><br><span class="line"> if (process.env.NODE_ENV !== 'production') {</span><br><span class="line"> initProxy(vm)</span><br><span class="line"> } else {</span><br><span class="line"> vm._renderProxy = vm</span><br><span class="line"> }</span><br><span class="line"> // expose real self</span><br><span class="line"> vm._self = vm</span><br><span class="line"> // 4、下面的以init开头的方法,都是根据options,给vm即this塞必要的属性方便开发时使用</span><br><span class="line"> // 以call开头的方法,都是在触发Vue的声明周期</span><br><span class="line"> initLifecycle(vm)</span><br><span class="line"> initEvents(vm)</span><br><span class="line"> initRender(vm)</span><br><span class="line"> callHook(vm, 'beforeCreate')</span><br><span class="line"> initInjections(vm) // resolve injections before data/props</span><br><span class="line"> initState(vm)</span><br><span class="line"> initProvide(vm) // resolve provide after data/props</span><br><span class="line"> callHook(vm, 'created')</span><br><span class="line"></span><br><span class="line"> /* istanbul ignore if */</span><br><span class="line"> if (process.env.NODE_ENV !== 'production' && config.performance && mark) {</span><br><span class="line"> vm._name = formatComponentName(vm, false)</span><br><span class="line"> mark(endTag)</span><br><span class="line"> measure(`vue ${vm._name} init`, startTag, endTag)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (vm.$options.el) {</span><br><span class="line"> vm.$mount(vm.$options.el) // 5、$mount方法,就是mountComponent方法,定义在 // vue/src/core/instance/lifecycle.js中</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li>
<li>执行<code>$mount</code>方法<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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></pre></td><td class="code"><pre><span class="line">// vue/src/core/instance/lifecycle.js</span><br><span class="line">export function mountComponent (</span><br><span class="line"> vm: Component, // 6、vm即this、即Vue Instance</span><br><span class="line"> el: ?Element, // 7、new Vue(options)时传入的el</span><br><span class="line"> hydrating?: boolean</span><br><span class="line">): Component {</span><br><span class="line"> vm.$el = el</span><br><span class="line"> if (!vm.$options.render) {</span><br><span class="line"> vm.$options.render = createEmptyVNode</span><br><span class="line"> if (process.env.NODE_ENV !== 'production') {</span><br><span class="line"> /* istanbul ignore if */</span><br><span class="line"> if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||</span><br><span class="line"> vm.$options.el || el) {</span><br><span class="line"> warn(</span><br><span class="line"> 'You are using the runtime-only build of Vue where the template ' +</span><br><span class="line"> 'compiler is not available. Either pre-compile the templates into ' +</span><br><span class="line"> 'render functions, or use the compiler-included build.',</span><br><span class="line"> vm</span><br><span class="line"> )</span><br><span class="line"> } else {</span><br><span class="line"> warn(</span><br><span class="line"> 'Failed to mount component: template or render function not defined.',</span><br><span class="line"> vm</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"> callHook(vm, 'beforeMount')</span><br><span class="line"></span><br><span class="line"> let updateComponent</span><br><span class="line"> /* istanbul ignore if */</span><br><span class="line"> if (process.env.NODE_ENV !== 'production' && config.performance && mark) {</span><br><span class="line"> updateComponent = () => {</span><br><span class="line"> const name = vm._name</span><br><span class="line"> const id = vm._uid</span><br><span class="line"> const startTag = `vue-perf-start:${id}`</span><br><span class="line"> const endTag = `vue-perf-end:${id}`</span><br><span class="line"></span><br><span class="line"> mark(startTag)</span><br><span class="line"> const vnode = vm._render()</span><br><span class="line"> mark(endTag)</span><br><span class="line"> measure(`vue ${name} render`, startTag, endTag)</span><br><span class="line"></span><br><span class="line"> mark(startTag)</span><br><span class="line"> vm._update(vnode, hydrating)</span><br><span class="line"> mark(endTag)</span><br><span class="line"> measure(`vue ${name} patch`, startTag, endTag)</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> // 8、定义updateComponent方法传给Watcher,</span><br><span class="line"> // 使用Watcher去watch options中定义的data,一旦data中的数据发生变动,则触发updateComponent</span><br><span class="line"> updateComponent = () => {</span><br><span class="line"> vm._update(vm._render(), hydrating)</span><br><span class="line"> // 9、_render方法即Vue.prototype._render,定义在 // vue/src/core/instance/render.js,</span><br><span class="line"> // 该方法调用了options中的render方法生成了vnode而vnode是VDOM的基础之一,而options中的render方法,则是在Vue Compile阶段</span><br><span class="line"> // 根据开发者定义的Vue Template生成的</span><br><span class="line"> // 10、_update方法即Vue.prototype._update,定义在 // vue/src/core/instance/lifecycle.js,</span><br><span class="line"> 该方法主要是执行patch方法,而patch方法第一个参数是preVnode,第二个参数是vnode。</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // we set this to vm._watcher inside the watcher's constructor</span><br><span class="line"> // since the watcher's initial patch may call $forceUpdate (e.g. inside child</span><br><span class="line"> // component's mounted hook), which relies on vm._watcher being already defined</span><br><span class="line"></span><br><span class="line"> // 11、new Watcher(xxx)时,将会在Watcher Constructor中调用传入的updateComponent方法,</span><br><span class="line"> 因此,当new Watcher(xxx)执行完毕,DOM操作已经执行完成,页面已经渲染出来了</span><br><span class="line"> new Watcher(vm, updateComponent, noop, { </span><br><span class="line"> before () {</span><br><span class="line"> if (vm._isMounted) {</span><br><span class="line"> callHook(vm, 'beforeUpdate')</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, true /* isRenderWatcher */)</span><br><span class="line"> hydrating = false</span><br><span class="line"></span><br><span class="line"> // manually mounted instance, call mounted on self</span><br><span class="line"> // mounted is called for render-created child components in its inserted hook</span><br><span class="line"> if (vm.$vnode == null) {</span><br><span class="line"> vm._isMounted = true</span><br><span class="line"> callHook(vm, 'mounted') // 12、第一次渲染完成,由此可以得出结论,Vue是以组件为单位进行watch的</span><br><span class="line"> // 那么,如果合理的对Vue代码进行封装,封装成组件,则有可能有效的减少VDOM DIFF时的节点数量</span><br><span class="line"> }</span><br><span class="line"> return vm</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="Vue-VDOM:通过比对VDOM,计算出所需要的真实DOM操作(总计19处注释)"><a href="#Vue-VDOM:通过比对VDOM,计算出所需要的真实DOM操作(总计19处注释)" class="headerlink" title="Vue VDOM:通过比对VDOM,计算出所需要的真实DOM操作(总计19处注释)"></a>Vue VDOM:通过比对VDOM,计算出所需要的真实DOM操作(总计19处注释)</h3><blockquote>
<p>VDOM其实是一个定义,是为了与真实DOM相区分而起的名字,不要被吓到,说白了,其实就是用JavaScript的Object(没错就是Vnode)为节点,构成的一棵树,每个节点上有parent、children以及其他信息,这棵树尽可能的与真实的DOM树相匹配,在数据被更新时,生成新VDOM Tree,与旧的VDOM Tree相比对,通过DIFF算法,比对出,需要怎么样操作DOM,才能正确更新</p>
</blockquote>
<ul>
<li><p>既然Vnode是基础,先看一下Vnode的数据结构</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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></pre></td><td class="code"><pre><span class="line">// vue/src/core/vdom/vnode.js</span><br><span class="line">export default class VNode {</span><br><span class="line"> tag: string | void; // 1、如vue-compoment-keep-alive</span><br><span class="line"> data: VNodeData | void; // 2、patch运行时用到的数据,比如hooks,keepAlive </span><br><span class="line"> children: ?Array<VNode>;</span><br><span class="line"> text: string | void;</span><br><span class="line"> elm: Node | void; // 3、真实的DOM节点</span><br><span class="line"> ns: string | void;</span><br><span class="line"> context: Component | void; // rendered in this component's scope</span><br><span class="line"> key: string | number | void;</span><br><span class="line"> componentOptions: VNodeComponentOptions | void; // 4、组件的options</span><br><span class="line"> componentInstance: Component | void; // component instance // 5、组件new完之后的Vue Instance</span><br><span class="line"> parent: VNode | void; // component placeholder node</span><br><span class="line"></span><br><span class="line"> // strictly internal</span><br><span class="line"> raw: boolean; // contains raw HTML? (server only)</span><br><span class="line"> isStatic: boolean; // hoisted static node</span><br><span class="line"> isRootInsert: boolean; // necessary for enter transition check</span><br><span class="line"> isComment: boolean; // empty comment placeholder?</span><br><span class="line"> isCloned: boolean; // is a cloned node?</span><br><span class="line"> isOnce: boolean; // is a v-once node?</span><br><span class="line"> asyncFactory: Function | void; // async component factory function</span><br><span class="line"> asyncMeta: Object | void;</span><br><span class="line"> isAsyncPlaceholder: boolean;</span><br><span class="line"> ssrContext: Object | void;</span><br><span class="line"> fnContext: Component | void; // real context vm for functional nodes</span><br><span class="line"> fnOptions: ?ComponentOptions; // for SSR caching</span><br><span class="line"> devtoolsMeta: ?Object; // used to store functional render context for devtools</span><br><span class="line"> fnScopeId: ?string; // functional scope id support</span><br><span class="line"></span><br><span class="line"> constructor (</span><br><span class="line"> tag?: string,</span><br><span class="line"> data?: VNodeData,</span><br><span class="line"> children?: ?Array<VNode>,</span><br><span class="line"> text?: string,</span><br><span class="line"> elm?: Node,</span><br><span class="line"> context?: Component,</span><br><span class="line"> componentOptions?: VNodeComponentOptions,</span><br><span class="line"> asyncFactory?: Function</span><br><span class="line"> ) {</span><br><span class="line"> this.tag = tag</span><br><span class="line"> this.data = data</span><br><span class="line"> this.children = children</span><br><span class="line"> this.text = text</span><br><span class="line"> this.elm = elm</span><br><span class="line"> this.ns = undefined</span><br><span class="line"> this.context = context</span><br><span class="line"> this.fnContext = undefined</span><br><span class="line"> this.fnOptions = undefined</span><br><span class="line"> this.fnScopeId = undefined</span><br><span class="line"> this.key = data && data.key</span><br><span class="line"> this.componentOptions = componentOptions</span><br><span class="line"> this.componentInstance = undefined</span><br><span class="line"> this.parent = undefined</span><br><span class="line"> this.raw = false</span><br><span class="line"> this.isStatic = false</span><br><span class="line"> this.isRootInsert = true</span><br><span class="line"> this.isComment = false</span><br><span class="line"> this.isCloned = false</span><br><span class="line"> this.isOnce = false</span><br><span class="line"> this.asyncFactory = asyncFactory</span><br><span class="line"> this.asyncMeta = undefined</span><br><span class="line"> this.isAsyncPlaceholder = false</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // DEPRECATED: alias for componentInstance for backwards compat.</span><br><span class="line"> /* istanbul ignore next */</span><br><span class="line"> get child (): Component | void {</span><br><span class="line"> return this.componentInstance</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li><p>先解释一下Patch,Patch是Vue VDOM部分中的一个函数,该函数就是<code>Vue.prototype.__patch__</code>,而该函数其实就是在做DIFF,输入是oldVnode、vnode,输出是DIFF后,需要对真实DOM节点所做对DOM操作,比如createElemennt、insertBefore、appendChild等等,而驱动该函数运行的,是Watcher,而Watcher观察的,是data。因此,开发者只需要改变Vue options中的data,就能完成想要的更新</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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></pre></td><td class="code"><pre><span class="line">// vue/src/core/vdom/patch.js</span><br><span class="line">function patch (oldVnode, vnode, hydrating, removeOnly) {</span><br><span class="line"> // 6、输入参数是oldVnode、vnode,就是对oldVnode和vnode做DIFF</span><br><span class="line"> if (isUndef(vnode)) {</span><br><span class="line"> if (isDef(oldVnode)) invokeDestroyHook(oldVnode)</span><br><span class="line"> return</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> let isInitialPatch = false</span><br><span class="line"> const insertedVnodeQueue = []</span><br><span class="line"></span><br><span class="line"> if (isUndef(oldVnode)) {</span><br><span class="line"> // empty mount (likely as component), create new root element</span><br><span class="line"> isInitialPatch = true</span><br><span class="line"> createElm(vnode, insertedVnodeQueue)</span><br><span class="line"> } else {</span><br><span class="line"> const isRealElement = isDef(oldVnode.nodeType)</span><br><span class="line"> if (!isRealElement && sameVnode(oldVnode, vnode)) {</span><br><span class="line"> // patch existing root node</span><br><span class="line"></span><br><span class="line"> // 7、开始做DIFF</span><br><span class="line"> patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)</span><br><span class="line"> } else {</span><br><span class="line"> if (isRealElement) {</span><br><span class="line"> // mounting to a real element</span><br><span class="line"> // check if this is server-rendered content and if we can perform</span><br><span class="line"> // a successful hydration.</span><br><span class="line"> if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {</span><br><span class="line"> oldVnode.removeAttribute(SSR_ATTR)</span><br><span class="line"> hydrating = true</span><br><span class="line"> }</span><br><span class="line"> if (isTrue(hydrating)) {</span><br><span class="line"> if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {</span><br><span class="line"> invokeInsertHook(vnode, insertedVnodeQueue, true)</span><br><span class="line"> return oldVnode</span><br><span class="line"> } else if (process.env.NODE_ENV !== 'production') {</span><br><span class="line"> warn(</span><br><span class="line"> 'The client-side rendered virtual DOM tree is not matching ' +</span><br><span class="line"> 'server-rendered content. This is likely caused by incorrect ' +</span><br><span class="line"> 'HTML markup, for example nesting block-level elements inside ' +</span><br><span class="line"> '<p>, or missing <tbody>. Bailing hydration and performing ' +</span><br><span class="line"> 'full client-side render.'</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> // either not server-rendered, or hydration failed.</span><br><span class="line"> // create an empty node and replace it</span><br><span class="line"> oldVnode = emptyNodeAt(oldVnode)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // replacing existing element</span><br><span class="line"> const oldElm = oldVnode.elm</span><br><span class="line"> const parentElm = nodeOps.parentNode(oldElm)</span><br><span class="line"></span><br><span class="line"> // create new node</span><br><span class="line"> createElm(</span><br><span class="line"> vnode,</span><br><span class="line"> insertedVnodeQueue,</span><br><span class="line"> // extremely rare edge case: do not insert if old element is in a</span><br><span class="line"> // leaving transition. Only happens when combining transition +</span><br><span class="line"> // keep-alive + HOCs. (#4590)</span><br><span class="line"> oldElm._leaveCb ? null : parentElm,</span><br><span class="line"> nodeOps.nextSibling(oldElm)</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> // update parent placeholder node element, recursively</span><br><span class="line"> if (isDef(vnode.parent)) {</span><br><span class="line"> let ancestor = vnode.parent</span><br><span class="line"> const patchable = isPatchable(vnode)</span><br><span class="line"> while (ancestor) {</span><br><span class="line"> for (let i = 0; i < cbs.destroy.length; ++i) {</span><br><span class="line"> cbs.destroy[i](ancestor)</span><br><span class="line"> }</span><br><span class="line"> ancestor.elm = vnode.elm</span><br><span class="line"> if (patchable) {</span><br><span class="line"> for (let i = 0; i < cbs.create.length; ++i) {</span><br><span class="line"> cbs.create[i](emptyNode, ancestor)</span><br><span class="line"> }</span><br><span class="line"> // #6513</span><br><span class="line"> // invoke insert hooks that may have been merged by create hooks.</span><br><span class="line"> // e.g. for directives that uses the "inserted" hook.</span><br><span class="line"> const insert = ancestor.data.hook.insert</span><br><span class="line"> if (insert.merged) {</span><br><span class="line"> // start at index 1 to avoid re-invoking component mounted hook</span><br><span class="line"> for (let i = 1; i < insert.fns.length; i++) {</span><br><span class="line"> insert.fns[i]()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> registerRef(ancestor)</span><br><span class="line"> }</span><br><span class="line"> ancestor = ancestor.parent</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // destroy old node</span><br><span class="line"> if (isDef(parentElm)) {</span><br><span class="line"> removeVnodes(parentElm, [oldVnode], 0, 0)</span><br><span class="line"> } else if (isDef(oldVnode.tag)) {</span><br><span class="line"> invokeDestroyHook(oldVnode)</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"> invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)</span><br><span class="line"> return vnode.elm</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li>
<li><p>我们来看一下patchVnode函数。整个VDOM树,每个节点都有parent和children,而patchVnode其实在做的是:比对oldVnode’s children与vnode’s children</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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></pre></td><td class="code"><pre><span class="line">function patchVnode (</span><br><span class="line"> oldVnode,</span><br><span class="line"> vnode,</span><br><span class="line"> insertedVnodeQueue,</span><br><span class="line"> ownerArray,</span><br><span class="line"> index,</span><br><span class="line"> removeOnly</span><br><span class="line"> ) {</span><br><span class="line"> if (oldVnode === vnode) {</span><br><span class="line"> return</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (isDef(vnode.elm) && isDef(ownerArray)) {</span><br><span class="line"> // clone reused vnode</span><br><span class="line"> vnode = ownerArray[index] = cloneVNode(vnode)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> const elm = vnode.elm = oldVnode.elm</span><br><span class="line"></span><br><span class="line"> if (isTrue(oldVnode.isAsyncPlaceholder)) {</span><br><span class="line"> if (isDef(vnode.asyncFactory.resolved)) {</span><br><span class="line"> hydrate(oldVnode.elm, vnode, insertedVnodeQueue)</span><br><span class="line"> } else {</span><br><span class="line"> vnode.isAsyncPlaceholder = true</span><br><span class="line"> }</span><br><span class="line"> return</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // reuse element for static trees.</span><br><span class="line"> // note we only do this if the vnode is cloned -</span><br><span class="line"> // if the new node is not cloned it means the render functions have been</span><br><span class="line"> // reset by the hot-reload-api and we need to do a proper re-render.</span><br><span class="line"> if (isTrue(vnode.isStatic) &&</span><br><span class="line"> isTrue(oldVnode.isStatic) &&</span><br><span class="line"> vnode.key === oldVnode.key &&</span><br><span class="line"> (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))</span><br><span class="line"> ) {</span><br><span class="line"> vnode.componentInstance = oldVnode.componentInstance</span><br><span class="line"> return</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> let i</span><br><span class="line"> const data = vnode.data</span><br><span class="line"> if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {</span><br><span class="line"> i(oldVnode, vnode)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> const oldCh = oldVnode.children</span><br><span class="line"> const ch = vnode.children</span><br><span class="line"> if (isDef(data) && isPatchable(vnode)) {</span><br><span class="line"> for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)</span><br><span class="line"> if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)</span><br><span class="line"> }</span><br><span class="line"> if (isUndef(vnode.text)) {</span><br><span class="line"> // 8、如果children都存在,则比对children</span><br><span class="line"> if (isDef(oldCh) && isDef(ch)) {</span><br><span class="line"> if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)</span><br><span class="line"> } else if (isDef(ch)) { // 9、如果只有vnode上存在children,则这些children都需被add</span><br><span class="line"> if (process.env.NODE_ENV !== 'production') {</span><br><span class="line"> checkDuplicateKeys(ch)</span><br><span class="line"> }</span><br><span class="line"> if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')</span><br><span class="line"> addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)</span><br><span class="line"> } else if (isDef(oldCh)) { // 10、如果只有oldVnode上存在children,则这些children都需被删除</span><br><span class="line"> removeVnodes(elm, oldCh, 0, oldCh.length - 1)</span><br><span class="line"> } else if (isDef(oldVnode.text)) {</span><br><span class="line"> nodeOps.setTextContent(elm, '') // 11、针对text 节点做单独处理</span><br><span class="line"> }</span><br><span class="line"> } else if (oldVnode.text !== vnode.text) { // 12、针对text节点做单独处理</span><br><span class="line"> nodeOps.setTextContent(elm, vnode.text)</span><br><span class="line"> }</span><br><span class="line"> if (isDef(data)) {</span><br><span class="line"> if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li>
<li><p>在看updateChildren之前,先看一个函数sameVnode,该函数的用途是,判断节点a和节点b的真实DOM节点是否一致</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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">function sameVnode (a, b) {</span><br><span class="line"> return (</span><br><span class="line"> a.key === b.key && ( // 13、这里会先判断key,因此设置一个合适的key的重要性不言而喻。</span><br><span class="line"> // 当前后两次Patch更新时,如果key不变,那么就不会产生不必要的DOM操作</span><br><span class="line"> (</span><br><span class="line"> a.tag === b.tag &&</span><br><span class="line"> a.isComment === b.isComment &&</span><br><span class="line"> isDef(a.data) === isDef(b.data) &&</span><br><span class="line"> !childrenIgnored(a) && !childrenIgnored(b) &&</span><br><span class="line"> sameInputType(a, b)</span><br><span class="line"> ) || (</span><br><span class="line"> isTrue(a.isAsyncPlaceholder) &&</span><br><span class="line"> a.asyncFactory === b.asyncFactory &&</span><br><span class="line"> isUndef(b.asyncFactory.error)</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></li>
<li><p>再看一下updateChildren函数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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></pre></td><td class="code"><pre><span class="line">// vue/src/core/vdom/patch.js</span><br><span class="line"> function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {</span><br><span class="line"> let oldStartIdx = 0</span><br><span class="line"> let newStartIdx = 0</span><br><span class="line"> let oldEndIdx = oldCh.length - 1</span><br><span class="line"> let oldStartVnode = oldCh[0]</span><br><span class="line"> let oldEndVnode = oldCh[oldEndIdx]</span><br><span class="line"> let newEndIdx = newCh.length - 1</span><br><span class="line"> let newStartVnode = newCh[0]</span><br><span class="line"> let newEndVnode = newCh[newEndIdx]</span><br><span class="line"> let oldKeyToIdx, idxInOld, vnodeToMove, refElm</span><br><span class="line"></span><br><span class="line"> // removeOnly is a special flag used only by <transition-group></span><br><span class="line"> // to ensure removed elements stay in correct relative positions</span><br><span class="line"> // during leaving transitions</span><br><span class="line"> const canMove = !removeOnly</span><br><span class="line"></span><br><span class="line"> if (process.env.NODE_ENV !== 'production') {</span><br><span class="line"> checkDuplicateKeys(newCh)</span><br><span class="line"> }</span><br><span class="line"> // 14、假设old children是[A, B, C, D]</span><br><span class="line"> // new children是[B, C, D]</span><br><span class="line"> // 那么,根据下面的循环,DIFF后的DOM操作是 removeChild(A)</span><br><span class="line"> // 其实,该函数的最终目的是:在尽量减少DOM操作的前提下,找出old children中能继续使用的真实DOM节点,</span><br><span class="line"> // 并保证new children中的节点能够正确的更新</span><br><span class="line"> while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {</span><br><span class="line"> if (isUndef(oldStartVnode)) {</span><br><span class="line"> oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left</span><br><span class="line"> } else if (isUndef(oldEndVnode)) {</span><br><span class="line"> oldEndVnode = oldCh[--oldEndIdx]</span><br><span class="line"> } else if (sameVnode(oldStartVnode, newStartVnode)) {</span><br><span class="line"> patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)</span><br><span class="line"> oldStartVnode = oldCh[++oldStartIdx]</span><br><span class="line"> newStartVnode = newCh[++newStartIdx]</span><br><span class="line"> } else if (sameVnode(oldEndVnode, newEndVnode)) {</span><br><span class="line"> patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)</span><br><span class="line"> oldEndVnode = oldCh[--oldEndIdx]</span><br><span class="line"> newEndVnode = newCh[--newEndIdx]</span><br><span class="line"> } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right</span><br><span class="line"> patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)</span><br><span class="line"> // 15、产生DOM操作insertBefore</span><br><span class="line"> canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))</span><br><span class="line"> oldStartVnode = oldCh[++oldStartIdx]</span><br><span class="line"> newEndVnode = newCh[--newEndIdx]</span><br><span class="line"> } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left</span><br><span class="line"> patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)</span><br><span class="line"> canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)</span><br><span class="line"> oldEndVnode = oldCh[--oldEndIdx]</span><br><span class="line"> newStartVnode = newCh[++newStartIdx]</span><br><span class="line"> } else {</span><br><span class="line"> if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)</span><br><span class="line"> // 16、如果定义了key,则时间复杂度为O(1),如果没有定义key,则时间复杂度为O(n)</span><br><span class="line"> idxInOld = isDef(newStartVnode.key)</span><br><span class="line"> ? oldKeyToIdx[newStartVnode.key]</span><br><span class="line"> : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)</span><br><span class="line"> if (isUndef(idxInOld)) { // New element</span><br><span class="line"> // 17、产生DOM操作appendChild or insertBefore</span><br><span class="line"> createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)</span><br><span class="line"> } else {</span><br><span class="line"> vnodeToMove = oldCh[idxInOld]</span><br><span class="line"> if (sameVnode(vnodeToMove, newStartVnode)) {</span><br><span class="line"> patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)</span><br><span class="line"> oldCh[idxInOld] = undefined</span><br><span class="line"> canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)</span><br><span class="line"> } else {</span><br><span class="line"> // same key but different element. treat as new element</span><br><span class="line"> createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> newStartVnode = newCh[++newStartIdx]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (oldStartIdx > oldEndIdx) {</span><br><span class="line"> refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm</span><br><span class="line"> // 18、产生DOM操作</span><br><span class="line"> addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)</span><br><span class="line"> } else if (newStartIdx > newEndIdx) {</span><br><span class="line"> // 19、产生DOM操作</span><br><span class="line"> removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li>
<li><p>总结一下整个过程</p>
<ul>
<li>在Vue.prototype._init()中,Watcher将会watch options data等关键位置</li>
<li>开发者代码中更新了options data</li>
<li>Watcher watch到改变,触发updateComponent</li>
<li>调用options中的render函数,生成新vnode</li>
<li>紧接着调用patch,与oldVnode做对比,输出是:需要对真实DOM所做的操作</li>
<li>更新完成</li>
</ul>
</li>
</ul>
<h3 id="Vue-Watcher:相当于大管家,运行时,触发各处的调用(总计18处注释)"><a href="#Vue-Watcher:相当于大管家,运行时,触发各处的调用(总计18处注释)" class="headerlink" title="Vue Watcher:相当于大管家,运行时,触发各处的调用(总计18处注释)"></a>Vue Watcher:相当于大管家,运行时,触发各处的调用(总计18处注释)</h3><ul>
<li><p>上面多次提到了Watcher,Watcher在Vue中的作用有很多,比如</p>
<ul>
<li>上面提到的options data,更新options data触发视图更新</li>
<li>computed特性,更新options data后,更新computed的属性绑定的视图</li>
<li>Vue中手动watch特性</li>
<li>Vuex的实现,也是借助于Watcher</li>
</ul>
</li>
<li><p>整个Watcher其实就是观察者模式,只不过,register是全自动的,不需要开发者关心,我们将重点关注是如何实现全自动register</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br></pre></td><td class="code"><pre><span class="line">// vue/src/core/observer/watcher.js</span><br><span class="line">export default class Watcher {</span><br><span class="line"> vm: Component; // 1、Vue实例</span><br><span class="line"> expression: string;</span><br><span class="line"> cb: Function;</span><br><span class="line"> id: number;</span><br><span class="line"> deep: boolean;</span><br><span class="line"> user: boolean;</span><br><span class="line"> lazy: boolean;</span><br><span class="line"> sync: boolean;</span><br><span class="line"> dirty: boolean;</span><br><span class="line"> active: boolean;</span><br><span class="line"> deps: Array<Dep>;</span><br><span class="line"> newDeps: Array<Dep>;</span><br><span class="line"> depIds: SimpleSet;</span><br><span class="line"> newDepIds: SimpleSet;</span><br><span class="line"> before: ?Function;</span><br><span class="line"> getter: Function; // 2、当监听到变动是,需要执行到函数</span><br><span class="line"> value: any;</span><br><span class="line"></span><br><span class="line"> constructor (</span><br><span class="line"> vm: Component,</span><br><span class="line"> expOrFn: string | Function,</span><br><span class="line"> cb: Function,</span><br><span class="line"> options?: ?Object,</span><br><span class="line"> isRenderWatcher?: boolean // 3、mountComponent函数执行时,会new Watcher(xxx)</span><br><span class="line"> ) {</span><br><span class="line"> this.vm = vm</span><br><span class="line"> if (isRenderWatcher) {</span><br><span class="line"> vm._watcher = this</span><br><span class="line"> }</span><br><span class="line"> vm._watchers.push(this)</span><br><span class="line"> // options</span><br><span class="line"> if (options) {</span><br><span class="line"> this.deep = !!options.deep</span><br><span class="line"> this.user = !!options.user</span><br><span class="line"> this.lazy = !!options.lazy</span><br><span class="line"> this.sync = !!options.sync</span><br><span class="line"> this.before = options.before</span><br><span class="line"> } else {</span><br><span class="line"> this.deep = this.user = this.lazy = this.sync = false</span><br><span class="line"> }</span><br><span class="line"> this.cb = cb</span><br><span class="line"> this.id = ++uid // uid for batching</span><br><span class="line"> this.active = true</span><br><span class="line"> this.dirty = this.lazy // for lazy watchers</span><br><span class="line"> this.deps = []</span><br><span class="line"> this.newDeps = []</span><br><span class="line"> this.depIds = new Set()</span><br><span class="line"> this.newDepIds = new Set()</span><br><span class="line"> this.expression = process.env.NODE_ENV !== 'production'</span><br><span class="line"> ? expOrFn.toString()</span><br><span class="line"> : ''</span><br><span class="line"> // parse expression for getter</span><br><span class="line"> if (typeof expOrFn === 'function') {</span><br><span class="line"> this.getter = expOrFn // 4、expOrFn即mountComponent函数中到updateComponent函数</span><br><span class="line"> } else {</span><br><span class="line"> this.getter = parsePath(expOrFn)</span><br><span class="line"> if (!this.getter) {</span><br><span class="line"> this.getter = noop</span><br><span class="line"> process.env.NODE_ENV !== 'production' && warn(</span><br><span class="line"> `Failed watching path: "${expOrFn}" ` +</span><br><span class="line"> 'Watcher only accepts simple dot-delimited paths. ' +</span><br><span class="line"> 'For full control, use a function instead.',</span><br><span class="line"> vm</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> this.value = this.lazy</span><br><span class="line"> ? undefined</span><br><span class="line"> : this.get() // 5、在Watcher构造函数中执行get()函数</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * Evaluate the getter, and re-collect dependencies.</span><br><span class="line"> */</span><br><span class="line"> get () {</span><br><span class="line"> pushTarget(this) // 6、此处是关键,将会使得 Dep.target === this</span><br><span class="line"> let value</span><br><span class="line"> const vm = this.vm</span><br><span class="line"> try {</span><br><span class="line"> value = this.getter.call(vm, vm) </span><br><span class="line"> // 6、第一次执行updateComponent函数,</span><br><span class="line"> // updateComponent会依次调用Vue.prototype._render()生成VDOM树、</span><br><span class="line"> // Vue.prototype._update()进行patch、patch()、产生DOM操作</span><br><span class="line"> } catch (e) {</span><br><span class="line"> if (this.user) {</span><br><span class="line"> handleError(e, vm, `getter for watcher "${this.expression}"`)</span><br><span class="line"> } else {</span><br><span class="line"> throw e</span><br><span class="line"> }</span><br><span class="line"> } finally {</span><br><span class="line"> // "touch" every property so they are all tracked as</span><br><span class="line"> // dependencies for deep watching</span><br><span class="line"> if (this.deep) {</span><br><span class="line"> traverse(value)</span><br><span class="line"> }</span><br><span class="line"> popTarget()</span><br><span class="line"> this.cleanupDeps()</span><br><span class="line"> }</span><br><span class="line"> return value</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * Add a dependency to this directive.</span><br><span class="line"> */</span><br><span class="line"> addDep (dep: Dep) {</span><br><span class="line"> const id = dep.id</span><br><span class="line"> if (!this.newDepIds.has(id)) {</span><br><span class="line"> this.newDepIds.add(id)</span><br><span class="line"> this.newDeps.push(dep)</span><br><span class="line"> if (!this.depIds.has(id)) {</span><br><span class="line"> dep.addSub(this) // 7、向Dep实例中添加 this ,</span><br><span class="line"> // 当被defineReactive后的字段(比如data中当字段)被更新,</span><br><span class="line"> // Dep类的notify()函数会被调用,紧接着会调用 this.update()</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"> * Clean up for dependency collection.</span><br><span class="line"> */</span><br><span class="line"> cleanupDeps () {</span><br><span class="line"> let i = this.deps.length</span><br><span class="line"> while (i--) {</span><br><span class="line"> const dep = this.deps[i]</span><br><span class="line"> if (!this.newDepIds.has(dep.id)) {</span><br><span class="line"> dep.removeSub(this)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> let tmp = this.depIds</span><br><span class="line"> this.depIds = this.newDepIds</span><br><span class="line"> this.newDepIds = tmp</span><br><span class="line"> this.newDepIds.clear()</span><br><span class="line"> tmp = this.deps</span><br><span class="line"> this.deps = this.newDeps</span><br><span class="line"> this.newDeps = tmp</span><br><span class="line"> this.newDeps.length = 0</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * Subscriber interface.</span><br><span class="line"> * Will be called when a dependency changes.</span><br><span class="line"> */</span><br><span class="line"> update () {</span><br><span class="line"> /* istanbul ignore else */</span><br><span class="line"> if (this.lazy) {</span><br><span class="line"> this.dirty = true</span><br><span class="line"> } else if (this.sync) {</span><br><span class="line"> this.run()</span><br><span class="line"> } else {</span><br><span class="line"> queueWatcher(this) // 8、将Watcher加入队列,</span><br><span class="line"> // 这是Vue异步渲染的原理,</span><br><span class="line"> // 队列中的Watcher将会在下一个tick中被调用this.run() 函数,</span><br><span class="line"> // this.run() 最终会调用 this.getter() 函数,</span><br><span class="line"> // 如果 this 是Vue组件实例对应的Watcher,</span><br><span class="line"> // 那么,就会触发updateComponent被调用</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"> * Scheduler job interface.</span><br><span class="line"> * Will be called by the scheduler.</span><br><span class="line"> */</span><br><span class="line"> run () {</span><br><span class="line"> if (this.active) {</span><br><span class="line"> const value = this.get()</span><br><span class="line"> if (</span><br><span class="line"> value !== this.value ||</span><br><span class="line"> // Deep watchers and watchers on Object/Arrays should fire even</span><br><span class="line"> // when the value is the same, because the value may</span><br><span class="line"> // have mutated.</span><br><span class="line"> isObject(value) ||</span><br><span class="line"> this.deep</span><br><span class="line"> ) {</span><br><span class="line"> // set new value</span><br><span class="line"> const oldValue = this.value</span><br><span class="line"> this.value = value</span><br><span class="line"> if (this.user) {</span><br><span class="line"> try {</span><br><span class="line"> this.cb.call(this.vm, value, oldValue)</span><br><span class="line"> } catch (e) {</span><br><span class="line"> handleError(e, this.vm, `callback for watcher "${this.expression}"`)</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> this.cb.call(this.vm, value, oldValue)</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"> * Evaluate the value of the watcher.</span><br><span class="line"> * This only gets called for lazy watchers.</span><br><span class="line"> */</span><br><span class="line"> evaluate () {</span><br><span class="line"> this.value = this.get()</span><br><span class="line"> this.dirty = false</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * Depend on all deps collected by this watcher.</span><br><span class="line"> */</span><br><span class="line"> depend () {</span><br><span class="line"> let i = this.deps.length</span><br><span class="line"> while (i--) {</span><br><span class="line"> this.deps[i].depend()</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"> * Remove self from all dependencies' subscriber list.</span><br><span class="line"> */</span><br><span class="line"> teardown () {</span><br><span class="line"> if (this.active) {</span><br><span class="line"> // remove self from vm's watcher list</span><br><span class="line"> // this is a somewhat expensive operation so we skip it</span><br><span class="line"> // if the vm is being destroyed.</span><br><span class="line"> if (!this.vm._isBeingDestroyed) {</span><br><span class="line"> remove(this.vm._watchers, this)</span><br><span class="line"> }</span><br><span class="line"> let i = this.deps.length</span><br><span class="line"> while (i--) {</span><br><span class="line"> this.deps[i].removeSub(this)</span><br><span class="line"> }</span><br><span class="line"> this.active = false</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li><p>另一个很重要的类是:Dep</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">// vue/src/core/observer/dep.js</span><br><span class="line">export default class Dep {</span><br><span class="line"> static target: ?Watcher; // 9、这是关键变量:Vue用该全局变量,</span><br><span class="line"> // 来保存当前正在watch的Watcher实例是哪个,这是依赖收集的关键</span><br><span class="line"> id: number;</span><br><span class="line"> subs: Array<Watcher>; // 10、该属性表示所有正在watch该Dep实例的Watcher。</span><br><span class="line"> // 一个Dep实例对应了一个data中的一个字段,</span><br><span class="line"> // 当该字段被更新,将会触发Watcher运行this.run()</span><br><span class="line"></span><br><span class="line"> constructor () {</span><br><span class="line"> this.id = uid++</span><br><span class="line"> this.subs = []</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> addSub (sub: Watcher) {</span><br><span class="line"> this.subs.push(sub)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> removeSub (sub: Watcher) {</span><br><span class="line"> remove(this.subs, sub)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> depend () {</span><br><span class="line"> if (Dep.target) {</span><br><span class="line"> Dep.target.addDep(this) // 11、该方法会调用Dep类的addSub方法,</span><br><span class="line"> // 最终的结果是,将Dep.target保存的Watcher实例加入到Dep的subs数组中,</span><br><span class="line"> // 后续更新字段触发Dep类的notify时,运行该Watcher实例</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> notify () { // 12、当被defineReactive后的字段更新后,notify会被执行</span><br><span class="line"> // stabilize the subscriber list first</span><br><span class="line"> const subs = this.subs.slice()</span><br><span class="line"> if (process.env.NODE_ENV !== 'production' && !config.async) {</span><br><span class="line"> // subs aren't sorted in scheduler if not running async</span><br><span class="line"> // we need to sort them now to make sure they fire in correct</span><br><span class="line"> // order</span><br><span class="line"> subs.sort((a, b) => a.id - b.id)</span><br><span class="line"> }</span><br><span class="line"> for (let i = 0, l = subs.length; i < l; i++) {</span><br><span class="line"> subs[i].update() //13、执行Watcher实例中的update()</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">// the current target watcher being evaluated.</span><br><span class="line">// this is globally unique because there could be only one</span><br><span class="line">// watcher being evaluated at any time.</span><br><span class="line">Dep.target = null // 14、该机制奏效的依据是:同一时刻,</span><br><span class="line">// 只会有一个Watcher在运行,而绝大多数情况下Watcher对应的就是Vue组件实例。</span><br><span class="line">// Vue的Patch更新机制,是以Vue组件为单位的</span><br><span class="line">const targetStack = []</span><br><span class="line"></span><br><span class="line">export function pushTarget (target: ?Watcher) { </span><br><span class="line"> targetStack.push(target)</span><br><span class="line"> Dep.target = target</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export function popTarget () {</span><br><span class="line"> targetStack.pop()</span><br><span class="line"> Dep.target = targetStack[targetStack.length - 1]</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li><p>接下来就是关键方法:defineReactive</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><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></pre></td><td class="code"><pre><span class="line">export function defineReactive (</span><br><span class="line"> obj: Object,</span><br><span class="line"> key: string,</span><br><span class="line"> val: any,</span><br><span class="line"> customSetter?: ?Function,</span><br><span class="line"> shallow?: boolean</span><br><span class="line">) {</span><br><span class="line"> const dep = new Dep() // 15、一个字段key,对应着一个Dep实例</span><br><span class="line"></span><br><span class="line"> const property = Object.getOwnPropertyDescriptor(obj, key)</span><br><span class="line"> if (property && property.configurable === false) {</span><br><span class="line"> return</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // cater for pre-defined getter/setters</span><br><span class="line"> const getter = property && property.get</span><br><span class="line"> const setter = property && property.set</span><br><span class="line"> if ((!getter || setter) && arguments.length === 2) {</span><br><span class="line"> val = obj[key]</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> let childOb = !shallow && observe(val)</span><br><span class="line"> // 16、Vue的关键点,使用了Object.defineProperty,</span><br><span class="line"> // 借助get/set机制,在get时,自动收集依赖,在set时自动触发更新</span><br><span class="line"> Object.defineProperty(obj, key, {</span><br><span class="line"> enumerable: true,</span><br><span class="line"> configurable: true,</span><br><span class="line"> get: function reactiveGetter () {</span><br><span class="line"> const value = getter ? getter.call(obj) : val</span><br><span class="line"> if (Dep.target) { // 17、如果当前有Watcher正在执行,</span><br><span class="line"> // 说明该字段key需要进行依赖收集,那么,收集依赖</span><br><span class="line"> dep.depend() </span><br><span class="line"> if (childOb) {</span><br><span class="line"> childOb.dep.depend()</span><br><span class="line"> if (Array.isArray(value)) {</span><br><span class="line"> dependArray(value)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return value</span><br><span class="line"> },</span><br><span class="line"> set: function reactiveSetter (newVal) {</span><br><span class="line"> const value = getter ? getter.call(obj) : val</span><br><span class="line"> /* eslint-disable no-self-compare */</span><br><span class="line"> if (newVal === value || (newVal !== newVal && value !== value)) {</span><br><span class="line"> return</span><br><span class="line"> }</span><br><span class="line"> /* eslint-enable no-self-compare */</span><br><span class="line"> if (process.env.NODE_ENV !== 'production' && customSetter) {</span><br><span class="line"> customSetter()</span><br><span class="line"> }</span><br><span class="line"> // #7981: for accessor properties without setter</span><br><span class="line"> if (getter && !setter) return</span><br><span class="line"> if (setter) {</span><br><span class="line"> setter.call(obj, newVal)</span><br><span class="line"> } else {</span><br><span class="line"> val = newVal</span><br><span class="line"> }</span><br><span class="line"> childOb = !shallow && observe(newVal)</span><br><span class="line"> dep.notify() // 18、如果该字段key被更新,运行Watcher</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="从产品角度进行总结"><a href="#从产品角度进行总结" class="headerlink" title="从产品角度进行总结"></a>从产品角度进行总结</h3><p>Vue的成功,有两个亮点可以学习:</p>
<ul>
<li>优化开发者开发体验,拉拢开发者,具体的表现有<ul>
<li>用户只需要写类似html的Vue template模版,入手容易</li>
<li>全自动watch,同时又不降低性能</li>
</ul>
</li>
<li>技术创新<ul>
<li>使用DIFF算法进行Patch更新</li>
<li>全自动Watch的设计</li>
<li>跨平台支持,横向扩大开发者队伍</li>
</ul>
</li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
<link itemprop="mainEntityOfPage" href="https://micsay.com/2020/01/08/get-start-on-vscode/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpeg">
<meta itemprop="name" content="崔">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="细水会流长">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2020/01/08/get-start-on-vscode/" class="post-title-link" itemprop="url">VS Code源码解析</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2020-01-08 23:20:46" itemprop="dateCreated datePublished" datetime="2020-01-08T23:20:46+08:00">2020-01-08</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-check-o"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2020-08-01 12:44:17" itemprop="dateModified" datetime="2020-08-01T12:44:17+08:00">2020-08-01</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E5%89%8D%E7%AB%AF/" itemprop="url" rel="index">
<span itemprop="name">前端</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<blockquote>
<p>特别声明:本文未经许可禁止转载</p>
</blockquote>
<h3 id="开头"><a href="#开头" class="headerlink" title="开头"></a>开头</h3><ul>
<li><code>VS Code</code>是微软在<code>Electron</code>的基础上使用TypeScript开发的</li>
<li>本文是基于VS Code 1.36.0版本的基础上分析的</li>
<li>本文重点解析VS Code工程中,我认为值得学习的地方</li>
<li>如有问题,可以留言</li>
</ul>
<h3 id="Chromium"><a href="#Chromium" class="headerlink" title="Chromium"></a>Chromium</h3><p>要讲<code>Electron</code>,必须先说<code>Chromium</code>。<code>Chromium</code>使用了多进程架构,分为<code>Browser Process</code>和<code>Render Process</code>,<code>Render Process</code>使用<code>Blink</code>和<code>V8</code>,<code>Blink</code>用于计算布局,<code>V8</code>用于运行<code>JavaScript</code>代码,真正渲染到屏幕上一个一个的像素,是在<code>Browser Process</code>完成的,<code>Browser Process</code>和<code>Render Process</code>通过<code>IPC进程通信</code>(使用Mojo),<code>Browser Process</code>可以保证安全(用于渲染到屏幕,管理Cookie、Storage、网络请求等),而<code>Render Process</code>是在沙箱里面运行的。</p>
<h3 id="Electron"><a href="#Electron" class="headerlink" title="Electron"></a>Electron</h3><p><code>Electron</code>在<code>Chromuim</code>基础上,给<code>Browser Process</code>和<code>Render Process</code>都加进了<code>Node Environment</code>,这样,带来了<code>Node</code>开发者,带来了丰富的NPM包,并且,不论是在<code>Browser Process</code>还是<code>Render Process</code>,都能直接调用<code>Node API</code>,从而获得Native能力。同时,<code>Electron</code>还给<code>Browser Process</code>和<code>Render Process</code>加进了<code>Electron API</code>,为开发者提供<code>Browser Process</code>和<code>Render Process</code>的IPC通信API,以及提供一些必要的功能。<br>以下用主进程表示Browser Process,用渲染进程表示Render Process</p>
<h3 id="为方便后文理解,先讲一下VS-Code初始化过程"><a href="#为方便后文理解,先讲一下VS-Code初始化过程" class="headerlink" title="为方便后文理解,先讲一下VS Code初始化过程"></a>为方便后文理解,先讲一下VS Code初始化过程</h3><blockquote>
<p>为方便起见,文件名不加后缀,比如<code>src/main</code>实际为<code>src/main.js</code>,而<code>src/vs/code/electron-main/main</code>实际为<code>src/vs/code/electron-main/main.ts</code></p>
</blockquote>
<ul>
<li>Electron根据根目录下package.json文件中的main字段,在主进程加载<code>src/main</code>,处理本地语言配置以及<code>process.env</code></li>
<li>加载<code>src/vs/code/electron-main/main</code>,实例化<code>CodeMain</code>类,调用该类中的<code>main()</code>方法,创建主进程中外层的<code>InstantiationService</code>,并实例化<code>CodeApplication</code>类,调用该类中的<code>startup()</code>方法<blockquote>
<p><code>InstantiationService</code>用于实例化其他类,使得其他类在主进程或者渲染进程中,在保持单例的同时又能很方便的作为构造器参数传入,这个类是VS Code工程中实现依赖注入的重要部分</p>
</blockquote>
</li>
<li>在<code>CodeApplication</code>类的<code>startup()</code>方法中,再次创建<code>InstantiationService</code>,该<code>InstantiationService</code>是外层<code>InstantiationService</code>的<code>child</code>,并且如果某个类的实例在当前窗口的<code>InstantiationService</code>中找不到时,会去外层的<code>InstantiationService</code>中查找,然后实例化各个<code>Service</code>类,并最终在<code>src/vs/code/electron-main/window</code>中调用<code>new BrowserWindow(options)</code>,打开窗口,携带处理完毕的配置参数加载渲染进程的代码<code>src/vs/code/electron-browser/workbench/workbench</code></li>
<li>加载<code>src/vs/workbench/electron-browser/main</code>,实例化渲染进程各个<code>Service</code>类放入<code>serviceCollection</code>,然后用<code>serviceCollection</code>去实例化渲染进程的<code>InstantiationService</code></li>
<li>加载后续代码,用TypeScript操作DOM,计算Layout,生成页面</li>
</ul>
<h3 id="用Service划分各个功能的界线"><a href="#用Service划分各个功能的界线" class="headerlink" title="用Service划分各个功能的界线"></a>用Service划分各个功能的界线</h3><p><code>VS Code</code>中有许多<code>Service</code>,有的位于主进程,有的位于渲染进程,有的只在主进程使用,有的只在渲染进程使用,有的在主进程中定义逻辑,在渲染进程中通过Electron提供的IPC建立Proxy使用(对于<code>Service</code>使用者来说无感知),<code>Service</code>位于<code>src/vs/platform</code>目录,主要有<code>IInstantiationService</code>,<code>IEnvironmentService</code>,<code>IFileService</code>,<code>ILayoutService</code>,<code>INotificationService</code>,<code>IOpenerService</code>,<code>IStorageService</code>,<code>IWindowsService</code>,<code>IWindowsMainService</code>,<code>IWorkspacesService</code>,<code>IWorkspacesMainService</code>等</p>
<h4 id="依赖注入Dependency-Injection"><a href="#依赖注入Dependency-Injection" class="headerlink" title="依赖注入Dependency Injection"></a>依赖注入Dependency Injection</h4><p>关于依赖注入的整体介绍,VS Code wiki已经讲的很清楚了:</p>
<blockquote>
<p>The code is organized around services of which most are defined in the <code>platform</code> layer. Services get to its clients via <code>constructor injection</code>.<br>A service definition is two parts: (1) the interface of a service, and (2) a service identifier - the latter is required because TypeScript doesn’t use nominal but structural typing. A service identifier is a decoration (as proposed for ES7) and should have the same name as the service interface.<br>Declaring a service dependency happens by adding a corresponding decoration to a constructor argument. In the snippet below <code>@IModelService</code> is the service identifier decoration and <code>IModelService</code> is the (optional) type annotation for this argument. When a dependency is optional, use the <code>@optional</code> decoration otherwise the instantiation service throws an error.</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">class Client {</span><br><span class="line"> constructor(</span><br><span class="line"> @IModelService modelService: IModelService, </span><br><span class="line"> @optional(IEditorService) editorService: IEditorService</span><br><span class="line"> ) {</span><br><span class="line"> // use services</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>Use the instantiation service to create instances for service consumers, like so <code>instantiationService.createInstance(Client)</code>. Usually, this is done for you when being registered as a contribution, like a Viewlet or Language.</p>
</blockquote>
<p>下面从代码角度说明一下:</p>
<ul>
<li>使用<code>decoration</code>(注解)将依赖以变量的形式存到<code>Class</code>上<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">// src/vs/platform/instantiation/common/instantiation.ts</span><br><span class="line">export function createDecorator<T>(serviceId: string): ServiceIdentifier<T> {</span><br><span class="line"></span><br><span class="line"> if (_util.serviceIds.has(serviceId)) {</span><br><span class="line"> return _util.serviceIds.get(serviceId)!;</span><br><span class="line"> }</span><br><span class="line"> //根据TypeScript的规定,实现注解函数</span><br><span class="line"> const id = <any>function (target: Function, key: string, index: number): any {</span><br><span class="line"> if (arguments.length !== 3) {</span><br><span class="line"> throw new Error('@IServiceName-decorator can only be used to decorate a parameter');</span><br><span class="line"> }</span><br><span class="line"> storeServiceDependency(id, target, index, false);</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> id.toString = () => serviceId;</span><br><span class="line"></span><br><span class="line"> _util.serviceIds.set(serviceId, id);</span><br><span class="line"> return id;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">function storeServiceDependency(id: Function, target: Function, index: number, optional: boolean): void {</span><br><span class="line"> // 在运行时,将注解保存到target(Class),方便之后计算graph</span><br><span class="line"> if (target[_util.DI_TARGET] === target) {</span><br><span class="line"> target[_util.DI_DEPENDENCIES].push({ id, index, optional });</span><br><span class="line"> } else {</span><br><span class="line"> target[_util.DI_DEPENDENCIES] = [{ id, index, optional }];</span><br><span class="line"> target[_util.DI_TARGET] = target;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>根据已有信息计算依赖,构造有向图</li>
<li>找出出度为0的节点,并从这些节点开始,用<code>instantiationService.createInstance(Client)</code>初始化实例<pre class=mermaid>
graph LR;
Class-A-->Dependence-Class-B;
Dependence-Class-B-->Dependence-Class-C;
Class-A-->Dependence-Class-D;
Dependence-Class-D-->Dependence-Class-E;
Dependence-Class-D-->Dependence-Class-F;
</pre>
<blockquote>
<p>其中,Class-A为当前需要实例化的类,graph生成完毕之后,根据规则,先实例化Dependence-Class-C、Dependence-Class-E、Dependence-Class-F,再实例化Dependence-Class-B、Dependence-Class-D,最后才实例化Class-A</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><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></pre></td><td class="code"><pre><span class="line">// src/vs/platform/instantiation/common/instantiationService.ts</span><br><span class="line">private _createAndCacheServiceInstance<T>(id: ServiceIdentifier<T>, desc: SyncDescriptor<T>, _trace: Trace): T {</span><br><span class="line"> type Triple = { id: ServiceIdentifier<any>, desc: SyncDescriptor<any>, _trace: Trace };</span><br><span class="line"> // 有向图,保存出度和入度</span><br><span class="line"> const graph = new Graph<Triple>(data => data.id.toString());</span><br><span class="line"></span><br><span class="line"> function throwCycleError() {</span><br><span class="line"> const err = new Error('[createInstance] cyclic dependency between services');</span><br><span class="line"> err.message = graph.toString();</span><br><span class="line"> throw err;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> let count = 0;</span><br><span class="line"> const stack = [{ id, desc, _trace }];</span><br><span class="line"> while (stack.length) {</span><br><span class="line"> const item = stack.pop()!;</span><br><span class="line"> graph.lookupOrInsertNode(item);</span><br><span class="line"></span><br><span class="line"> // TODO@joh use the graph to find a cycle</span><br><span class="line"> // a weak heuristic for cycle checks</span><br><span class="line"> if (count++ > 100) {</span><br><span class="line"> throwCycleError();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // check all dependencies for existence and if they need to be created first</span><br><span class="line"> let dependencies = _util.getServiceDependencies(item.desc.ctor);</span><br><span class="line"> for (let dependency of dependencies) {</span><br><span class="line"></span><br><span class="line"> let instanceOrDesc = this._getServiceInstanceOrDescriptor(dependency.id);</span><br><span class="line"> if (!instanceOrDesc && !dependency.optional) {</span><br><span class="line"> console.warn(`[createInstance] ${id} depends on ${dependency.id} which is NOT registered.`);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (instanceOrDesc instanceof SyncDescriptor) {</span><br><span class="line"> const d = { id: dependency.id, desc: instanceOrDesc, _trace: item._trace.branch(dependency.id, true) };</span><br><span class="line"> // 从item节点指向d节点</span><br><span class="line"> graph.insertEdge(item, d);</span><br><span class="line"> stack.push(d);</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"> while (true) {</span><br><span class="line"> // 找出出度为0的节点</span><br><span class="line"> let roots = graph.roots();</span><br><span class="line"></span><br><span class="line"> // if there is no more roots but still</span><br><span class="line"> // nodes in the graph we have a cycle</span><br><span class="line"> if (roots.length === 0) {</span><br><span class="line"> if (!graph.isEmpty()) {</span><br><span class="line"> throwCycleError();</span><br><span class="line"> }</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> for (let { data } of roots) {</span><br><span class="line"> // create instance and overwrite the service collections</span><br><span class="line"> const instance = this._createServiceInstanceWithOwner(data.id, data.desc.ctor, data.desc.staticArguments, data.desc.supportsDelayedInstantiation, data._trace);</span><br><span class="line"> this._setServiceInstance(data.id, instance);</span><br><span class="line"> graph.removeNode(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return <T>this._getServiceInstanceOrDescriptor(id);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li>
<li>值得说明的是,实例化是支持懒加载的,懒加载使用代理模式,懒加载的实现原理如下:<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">private _createServiceInstance<T>(ctor: any, args: any[] = [], _supportsDelayedInstantiation: boolean, _trace: Trace): T {</span><br><span class="line"> if (!_supportsDelayedInstantiation || !_canUseProxy) {</span><br><span class="line"> // eager instantiation or no support JS proxies (e.g. IE11)</span><br><span class="line"> return this._createInstance(ctor, args, _trace);</span><br><span class="line"></span><br><span class="line"> } else {</span><br><span class="line"> // Return a proxy object that's backed by an idle value. That</span><br><span class="line"> // strategy is to instantiate services in our idle time or when actually</span><br><span class="line"> // needed but not when injected into a consumer</span><br><span class="line"> const idle = new IdleValue(() => this._createInstance<T>(ctor, args, _trace));</span><br><span class="line"> return <T>new Proxy(Object.create(null), {</span><br><span class="line"> get(_target: T, prop: PropertyKey): any {</span><br><span class="line"> return idle.getValue()[prop];</span><br><span class="line"> },</span><br><span class="line"> set(_target: T, p: PropertyKey, value: any): boolean {</span><br><span class="line"> idle.getValue()[p] = value;</span><br><span class="line"> return true;</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>
</li>
</ul>
<h3 id="Part"><a href="#Part" class="headerlink" title="Part"></a>Part</h3><p>打开VS Code并新建一个窗口(默认配置下),可以将窗口分成几大部分:</p>
<ul>
<li>TitleBarPart,位于顶部</li>
<li>ActivityBarPart,位于最左侧,大部分由Icon构成</li>
<li>SideBarPart,紧贴ActiviyBarPart右侧</li>
<li>EditorPart,编辑器</li>
<li>PanelPart,位于编辑器下面,由Terminal等构成</li>
<li>StatusBarPart,位于最下面,显示状态、分支等<br>可见,VS Code视图由Part构成。<code>Part</code>是VS Code工程中的一个基础类,定义了许多抽象方法,其中,<code>protected createContentArea(parent: HTMLElement, options?: object): HTMLElement | null</code>方法,使用TypeScript操作DOM来用来定义视图</li>
</ul>
<h4 id="Part之用TypeScript操作DOM"><a href="#Part之用TypeScript操作DOM" class="headerlink" title="Part之用TypeScript操作DOM"></a>Part之用TypeScript操作DOM</h4><p>在<code>src/vs/base/browser/ui</code>目录下,定义了许多基础的组件,比如<code>SelectBox</code>,用<code>dom.append(container, $('.option-text'));</code>形式和CSS,定义界面。</p>
<h3 id="Command机制"><a href="#Command机制" class="headerlink" title="Command机制"></a>Command机制</h3><p>Command可以说是VS Code定义的另一个非常好用的概念。他可以让用户通过<code>Shift+Command+P</code>选择Command然后执行,并且赋予了<code>VS Code Extension</code>扩展Command的能力。Command支持插件进程和VS Code进程相互调用。</p>
<h3 id="Extension(插件)机制"><a href="#Extension(插件)机制" class="headerlink" title="Extension(插件)机制"></a>Extension(插件)机制</h3><p>软件开发中的开闭原则:开放扩展,关闭修改。Extension便是开闭原则的一个很好的实现。Chrome有插件,Cocos有插件,Hexo有插件,Webpack有插件,Gulp有插件,VS Code也有插件</p>
<p>VS Code内置插件在<code>extension</code>目录下,内置插件分成两种,一种是本地内置插件,另一种是打包是从Extension Markets下载的内置插件,插件开发文档<a href="https://code.visualstudio.com/api" target="_blank" rel="noopener">点这</a>。从插件大类来看,也可以分成两种,一种是<code>Normal Extension</code>,可以使用VS Code API,另一种是<code>Debugger Extension</code>,用于运行Debug Adapter。</p>
<h3 id="Gulp编译打包"><a href="#Gulp编译打包" class="headerlink" title="Gulp编译打包"></a>Gulp编译打包</h3><p>Gulp官方介绍如下:</p>
<blockquote>
<ul>
<li>Automation - gulp is a toolkit that helps you automate painful or time-consuming tasks in your development workflow.</li>
<li>Platform-agnostic - Integrations are built into all major IDEs and people are using gulp with PHP, .NET, Node.js, Java, and other platforms.</li>
<li>Strong Ecosystem - Use npm modules to do anything you want + over 2000 curated plugins for streaming file transformations</li>
<li>Simple - By providing only a minimal API surface, gulp is easy to learn and simple to use</li>
</ul>
</blockquote>
<p>VS Code打包脚本位于<code>build</code>目录下,在执行<code>gulp watch</code>之后,gulp会首先加载根目录的<code>gulpfile.js</code>文件,进而加载<code>build</code>目录下一系列<code>gulp.*.js</code>文件,<code>build/gulp.*.js</code>文件中定义了许多<code>gulp task</code>,各个task可以相互依赖。如果想运行VS Code,可以参考[官方文档](<a href="https://github.com/microsoft/VS" target="_blank" rel="noopener">https://github.com/microsoft/VS</a> Code/wiki/How-to-Contribute)。</p>
<h3 id="VS-Code调试架构"><a href="#VS-Code调试架构" class="headerlink" title="VS Code调试架构"></a>VS Code调试架构</h3><p>VS Code可以调试<code>javascript</code>、<code>python</code>、<code>php</code>、<code>c</code>各种语言,而实现这些调试等基础就是<code>DAP</code>协议,官方对<code>DAP</code>的图示如下:</p>
<p><img src="/images/debug-arch1.png" alt="dap"></p>
<p>VS Code 定义了一种抽象的协议即DAP,并实现了一种通用的调试UI,VS Code使用该协议与各种语言的调试进程通信,但是,各种语言不会实现DAP协议,因此,需要一个Adapter,即<code>Debug Adapter(DA)</code>,DA运行在一个单独的进程里面,与调试进程通信。<br>如果你想调试某种语言,首先,需要先实现该语言的<code>Debug Adapter</code>并以<code>Debugger Extension</code>的形式,安装到VS Code上,关于如何实现,你可以查看<a href="https://code.visualstudio.com/api/extension-guides/debugger-extension" target="_blank" rel="noopener">官方文档</a>。当然,大部分语言的<code>Debug Adapter</code>都已经被实现,你可以直接使用。</p>
<h3 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h3><blockquote>
<p><a href="https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle</a><br><a href="https://www.typescriptlang.org/docs/handbook/decorators.html" target="_blank" rel="noopener">https://www.typescriptlang.org/docs/handbook/decorators.html</a><br><a href="https://github.com/microsoft/vscode/wiki/Source-Code-Organization" target="_blank" rel="noopener">https://github.com/microsoft/vscode/wiki/Source-Code-Organization</a><br><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy</a><br><a href="https://code.visualstudio.com/api/extension-guides/debugger-extension" target="_blank" rel="noopener">https://code.visualstudio.com/api/extension-guides/debugger-extension</a></p>
</blockquote>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block home" lang="zh-Hans">
<link itemprop="mainEntityOfPage" href="https://micsay.com/2020/01/07/how-to-learn-a-project/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.jpeg">
<meta itemprop="name" content="崔">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="细水会流长">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/2020/01/07/how-to-learn-a-project/" class="post-title-link" itemprop="url">怎样选择并学习一个开源项目</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2020-01-07 23:22:23" itemprop="dateCreated datePublished" datetime="2020-01-07T23:22:23+08:00">2020-01-07</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-check-o"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2020-08-01 13:05:31" itemprop="dateModified" datetime="2020-08-01T13:05:31+08:00">2020-08-01</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/%E6%96%B9%E6%B3%95/" itemprop="url" rel="index">
<span itemprop="name">方法</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h3 id="怎样选择一个开源项目去学习"><a href="#怎样选择一个开源项目去学习" class="headerlink" title="怎样选择一个开源项目去学习"></a>怎样选择一个开源项目去学习</h3><ul>
<li>文档要丰富,包括user guide,document api,从clone到运行环境配置,debug</li>
<li>有测试用例,如果一个项目没有测试用例,那么这个项目最好不要去看</li>
<li>blog post丰富</li>
<li>有需求,能用得上,如果用不上,很快就忘记了</li>
</ul>
<h3 id="怎样学习一个开源项目"><a href="#怎样学习一个开源项目" class="headerlink" title="怎样学习一个开源项目"></a>怎样学习一个开源项目</h3><ul>
<li>首先,网速要快。由于众所周知的原因,我们需要一个工具,跨过山和大海,推荐<code>clashx/clash</code></li>
<li>详细阅读项目文档,design document等,如果是英文,最好直接自己阅读英文,而不是看别人已经翻译好的</li>
<li>不要使用windows,选择mac或者linux</li>
<li>配置好运行环境,能够debug,能够使用代码跳转,mac上推荐<code>vscode</code></li>
<li>从sample开始,然后是测试用例,一点一点分解</li>
<li>如果改项目支持插件开发,则一定要去完整的看一遍插件开发流程,并尝试开发一个插件</li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
</div>
</div>
<script>
window.addEventListener('tabs:register', () => {
let activeClass = CONFIG.comments.activeClass;
if (CONFIG.comments.storage) {
activeClass = localStorage.getItem('comments_active') || activeClass;
}
if (activeClass) {
let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
if (activeTab) {
activeTab.click();
}
}
});
if (CONFIG.comments.storage) {
window.addEventListener('tabs:click', event => {
if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
let commentClass = event.target.classList[1];
localStorage.setItem('comments_active', commentClass);
});
}
</script>
</div>
<div class="toggle sidebar-toggle">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
<aside class="sidebar">
<div class="sidebar-inner">
<ul class="sidebar-nav motion-element">
<li class="sidebar-nav-toc">
文章目录
</li>
<li class="sidebar-nav-overview">
站点概览
</li>
</ul>
<!--noindex-->
<div class="post-toc-wrap sidebar-panel">
</div>
<!--/noindex-->
<div class="site-overview-wrap sidebar-panel">
<div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
<img class="site-author-image" itemprop="image" alt="崔"
src="/images/avatar.jpeg">
<p class="site-author-name" itemprop="name">崔</p>
<div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
<nav class="site-state">
<div class="site-state-item site-state-posts">
<a href="/archives/">
<span class="site-state-item-count">4</span>
<span class="site-state-item-name">日志</span>
</a>
</div>
<div class="site-state-item site-state-categories">
<a href="/categories/">
<span class="site-state-item-count">2</span>
<span class="site-state-item-name">分类</span></a>
</div>
<div class="site-state-item site-state-tags">
<a href="/tags/">
<span class="site-state-item-count">3</span>
<span class="site-state-item-name">标签</span></a>
</div>
</nav>
</div>
<div class="links-of-author motion-element">
<span class="links-of-author-item">
<a href="https://github.com/effectivecui" title="GitHub → https://github.com/effectivecui" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i>GitHub</a>
</span>
<span class="links-of-author-item">
<a href="mailto:[email protected]" title="E-Mail → mailto:[email protected]" rel="noopener" target="_blank"><i class="fa fa-fw fa-envelope"></i>E-Mail</a>
</span>
<span class="links-of-author-item">
<a href="https://weibo.com/5258674355" title="Weibo → https://weibo.com/5258674355" rel="noopener" target="_blank"><i class="fa fa-fw fa-weibo"></i>Weibo</a>
</span>
<span class="links-of-author-item">
<a href="/atom.xml" title="RSS → /atom.xml"><i class="fa fa-fw fa-rss"></i>RSS</a>
</span>
</div>
<div class="links-of-blogroll motion-element">
<div class="links-of-blogroll-title">
<i class="fa fa-fw fa-wechat"></i>
欢迎关注公众号
</div>
<ul class="links-of-blogroll-list">
<li class="links-of-blogroll-item">
<div style="display: inline-block;">
<img src="/images/qrcode.jpg" alt="">
<p></p>
</div>
</li>
</ul>
</div>
</div>
</div>
</aside>
<div id="sidebar-dimmer"></div>
</div>
</main>
<footer class="footer">
<div class="footer-inner">
<div class="copyright">
©
<span itemprop="copyrightYear">2020</span>
<span class="with-love">
<i class="fa fa-user"></i>
</span>
<span class="author" itemprop="copyrightHolder">崔</span>
</div>
<div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> 强力驱动
</div>
<span class="post-meta-divider">|</span>
<div class="theme-info">主题 – <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a>
</div>
</div>
</footer>
</div>
<script src="//cdn.jsdelivr.net/npm/[email protected]/lib/anime.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/velocity-animate@1/velocity.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/velocity-animate@1/velocity.ui.min.js"></script>
<script src="/js/utils.js"></script>
<script src="/js/motion.js"></script>
<script src="/js/schemes/pisces.js"></script>
<script src="/js/next-boot.js"></script>
<script src="/js/bookmark.js"></script>
<script src="/js/local-search.js"></script>
<script>
if (document.querySelectorAll('pre.mermaid').length) {
NexT.utils.getScript('//cdn.jsdelivr.net/npm/mermaid@8/dist/mermaid.min.js', () => {
mermaid.initialize({
theme: 'forest',
logLevel: 3,
flowchart: { curve: 'linear' },
gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorMargin: 50 }
});
}, window.mermaid);
}
</script>
</body>
</html>