-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
1317 lines (1265 loc) · 56.2 KB
/
atom.xml
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
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://feiyayshx.github.io/web-architecture</id>
<title>墨眉</title>
<updated>2021-03-24T01:26:10.645Z</updated>
<generator>https://github.com/jpmonette/feed</generator>
<link rel="alternate" href="https://feiyayshx.github.io/web-architecture"/>
<link rel="self" href="https://feiyayshx.github.io/web-architecture/atom.xml"/>
<subtitle>温故而知新</subtitle>
<logo>https://feiyayshx.github.io/web-architecture/images/avatar.png</logo>
<icon>https://feiyayshx.github.io/web-architecture/favicon.ico</icon>
<rights>All rights reserved 2021, 墨眉</rights>
<entry>
<title type="html"><![CDATA[Vue 核心原理及开发总结]]></title>
<id>https://feiyayshx.github.io/web-architecture/post/vue-he-xin-yuan-li-ji-kai-fa-zong-jie/</id>
<link href="https://feiyayshx.github.io/web-architecture/post/vue-he-xin-yuan-li-ji-kai-fa-zong-jie/">
</link>
<updated>2021-02-11T06:15:14.000Z</updated>
<content type="html"><![CDATA[<h3 id="babelrc-配置">.babelrc 配置</h3>
<pre><code>{
// 预设,插件的集合
"presets": [
// 把es6转化为es5
"@babel/preset-env"
]
}
</code></pre>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[H5开发-常用功能实现方案汇总]]></title>
<id>https://feiyayshx.github.io/web-architecture/post/h5-kai-fa-chang-yong-gong-neng-shi-xian-fang-an-hui-zong/</id>
<link href="https://feiyayshx.github.io/web-architecture/post/h5-kai-fa-chang-yong-gong-neng-shi-xian-fang-an-hui-zong/">
</link>
<updated>2021-02-02T06:12:36.000Z</updated>
<content type="html"><![CDATA[<h3 id="h5实现一键拨号的电话拨打功能">h5实现一键拨号的电话拨打功能</h3>
<pre><code><a href="tel:13764567708">移动WEB页面一键拨打号码</a>
// qq 浏览器兼容性不好
</code></pre>
<h3 id="h5实现一键发送短信功能">h5实现一键发送短信功能</h3>
<pre><code><a href="sms:13764567708">移动WEB页面一键发送短信</a>
// qq 浏览器兼容性不好
</code></pre>
<h3 id="移动web页面自动探测电话号码">移动web页面自动探测电话号码</h3>
<pre><code><meta name="format-detection" content="telephone=yes">
<meta http-equiv="x-rim-auto-match" content="none">
</code></pre>
<h3 id="使用wtai协议进行拨打电话">使用wtai协议进行拨打电话</h3>
<pre><code><a href="wtai://wp//mc;13764567708">拨打10086 </a>
< a href="wtai://wp/ap;13764567708;">将10086存储至电话簿 </a>
</code></pre>
<h3 id="h5页面调用高德h5地图">h5页面调用高德h5地图</h3>
<pre><code>在调用处,打开该链接地址,传入参入
https://uri.amap.com/marker?position=116.473195,39.993253&name=地址&callnative=1
</code></pre>
<p><a href="https://developer.amap.com/api/uri-api/summary/">高德h5地图开发文档</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[移动端布局相关概念及术语]]></title>
<id>https://feiyayshx.github.io/web-architecture/post/h5-word/</id>
<link href="https://feiyayshx.github.io/web-architecture/post/h5-word/">
</link>
<updated>2020-10-26T02:27:01.000Z</updated>
<summary type="html"><![CDATA[<p>✊一大堆概念来了,别再傻傻分不清什么是像素、分辨率、PPI、DPI、DPR、视口... 一文扫盲,不再迷茫!</p>
]]></summary>
<content type="html"><![CDATA[<p>✊一大堆概念来了,别再傻傻分不清什么是像素、分辨率、PPI、DPI、DPR、视口... 一文扫盲,不再迷茫!</p>
<!-- more -->
<h2 id="1-像素">1. 像素</h2>
<p><strong>像素</strong>:是组成图像的最小单位,每一个像素都有特定的位置和色彩值。</p>
<p>图片、电子屏幕都是由一个个像素组成的。</p>
<h3 id="11-物理像素">1.1 物理像素</h3>
<p><strong>物理像素</strong>:也叫<strong>设备像素</strong>, 指设备上真实的物理单元。</p>
<p>例如:屏幕显示器的像素点,打印机的墨点等。</p>
<h3 id="12-逻辑像素">1.2 逻辑像素</h3>
<p>逻辑像素:也叫设备独立像素(Device Independent Pixels , 简写:DIP或DP )。 反应在css/js程序中的像素点。</p>
<p>所以, CSS像素是逻辑像素的一种。<br>
当描述一张图片宽高时,一般用 200px * 100px,这里的px也是逻辑像素。</p>
<h2 id="2-英寸">2. 英寸</h2>
<p>英寸:一般用来描述屏幕的物理大小。</p>
<p>例如:电脑显示器13英寸,15英寸等,手机显示器5.7英寸。 这里的英寸大小指的是屏幕的对角线长度。</p>
<h2 id="3-分辨率">3. 分辨率</h2>
<h3 id="31-屏幕分辨率">3.1 屏幕分辨率</h3>
<p>屏幕分辨率指一个屏幕有多少个像素点组成。</p>
<p>例如:iPhone XS Max 分辨率为2688 x 1242像素,这表示手机分别在垂直和水平上所具有的像素点数。</p>
<p><strong>当然分辨率高不代表屏幕就清晰,屏幕的清晰程度还与尺寸有关。</strong></p>
<h3 id="32-图像分辨率">3.2 图像分辨率</h3>
<p>图片分辨率其实是指图片含有的像素数。</p>
<p>例如:一张图片的分辨率为800 x 400,这表示图片分别在垂直和水平上所具有的像素点数为800和400。</p>
<p><strong>同一尺寸的图片,分辨率越高,图片越清晰。</strong></p>
<h2 id="4-ppi-pixel-per-inch">4. PPI (pixel per Inch)</h2>
<p>PPI: 每英寸包含的像素数。</p>
<p>PPI 可以用来描述屏幕的清晰度或图片的质量。<br>
当PPI 描述屏幕时,PPI越高,屏幕越清晰;当PPI 描述图片时,PPI越高,图片质量越高。</p>
<p>例如:iPhone XS Max 和 iPhone SE的PPI分别为458和326,说明iPhone XS Max比 iPhone SE的屏幕更清晰。</p>
<h2 id="5-dpidot-per-inch">5. DPI(Dot per Inch)</h2>
<p>DPI: 每英寸包含的点数。这里的点是一个抽象单位,可以是屏幕的像素点,图片的像素点,或者打印机的墨点。</p>
<p>当DPI描述屏幕和图片时,则等价于PPI。<br>
当DPI描述打印机时,则表示打印机每英寸可以打印的点数。</p>
<p>打印机的DPI越高,打印图像的精细程度就越高,同时这也会消耗更多的墨点和时间。</p>
<h2 id="6-dpr-device-pixel-ratio">6. DPR (Device Pixel Ratio)</h2>
<p>设备像素比:物理像素/设备独立像素。</p>
<p>在web中,通过浏览器提供的window.devicePixelRatio 获取dpr;</p>
<p>在css中,通过媒体查询min-device-pixel-ratio区分dpr;</p>
<h2 id="7-移动端开发">7. 移动端开发</h2>
<p>在iOS、Android和React Native开发中样式单位其实都使用的是设备独立像素。</p>
<p>在使用RN开发app, 或者开发h5时,UI设计一般是基于iphone6的像素设计的。iphone6的物理像素750*1334,设备像素比是2。为了适配所有机型,在写样式时需要把物理像素转为设备独立像素。<br>
例如:给定一个元素,宽度300px,样式布局时的宽度就是150px = 300px/2; 所以,为了减少计算,设计图可以直接按设备独立像素给出。</p>
<h2 id="8-web端开发">8. web端开发</h2>
<p>在PC端开发时,当页面正常情况下,即缩放比例100%,一个css像素等于一个设备独立像素;<br>
当用户对浏览器进行了放大,css像素也会放大,一个css像素会跨越更多的物理像素;<br>
<strong>页面放大比例 = css像素/设备独立像素</strong></p>
<h2 id="9-retina-屏幕">9. Retina 屏幕</h2>
<p>Retina 是一种高分辨率的显示标准,把更多的像素点压缩至一块屏幕里,从而达到更高的分辨率,并提高屏幕显示的细腻程度。也称为视网膜显示屏。在正常观看距离下足以使人肉眼无法分辨其中的单独像素。</p>
<p>第三代iPad发布会上,苹果给出了Retina设计标准的公式:<br>
<img src="https://feiyayshx.github.io/web-architecture/post-images/1603701724180.png" alt="" loading="lazy"></p>
<p>其中 a代表人眼视角,h 代表像素间距,d代表肉眼与屏幕的距离。符合以上条件的屏幕可以使肉眼看不见单个物理像素点。这样的IPS屏幕就可被苹果称作“Retina显示屏”。</p>
<p>它不能单纯的表达分辨率和PPI,只是表达视觉效果。</p>
<p>让多个物理像素渲染一个独立像素只是Retina屏幕为了达到效果而使用的一种技术。而不是所有dpr > 1的屏幕就是Retina屏幕。</p>
<p>例如:给你一块超大尺寸的屏幕,即使它的PPI很高,DPR也很高,在近距离你也能看清它的像素点,这就不算Retina屏幕。</p>
<p>常见地我们使用K和P来描述屏幕的大小或清晰度:</p>
<p>K代表屏幕横向有几个1024个像素,一般来讲横向像素超过2048就属于2K屏,横向像素超过4096就属于4K屏。</p>
<p>P代表的就是屏幕纵向的像素个数,1080P即纵向有1080个像素,分辨率为1920X1080的屏幕就属于1080P屏幕。</p>
<p>所谓的高清屏其实就是屏幕的物理分辨率达到或超过1920X1080的屏幕。</p>
<h2 id="10-viewport-视口">10. viewport (视口)</h2>
<p>视口(viewport)代表当前可见的计算机图形区域。在 Web 浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的 UI, 菜单栏等——即指你正在浏览的文档的那一部分。</p>
<p>一般视口有三种:布局视口、视觉视口、理想视口。</p>
<blockquote>
<p>viewport 大小是可变的,因为viewport 的宽度并不总是窗口的宽度。</p>
</blockquote>
<h3 id="101-布局视口layout-viewport">10.1 布局视口(layout viewport)</h3>
<p>布局视口是网页布局的基准窗口。</p>
<p>在PC端,布局视口等于浏览器窗口(不包括borders,margins,滚动条,浏览器框架);</p>
<p>在移动端,布局视口往往大于浏览器窗口,一般给定一个默认值<strong>980px</strong>, 能够让PC的网页可以在手机浏览器上呈现,但是非常小,用户可以手动对网页进行放大。</p>
<p>我们通过<code>document.documentElement.clientWidth/clientHeight</code> 获取布局视口的大小。</p>
<h3 id="102-视觉视口visual-viewport">10.2 视觉视口(visual viewport)</h3>
<p>视觉视口是用户在浏览器中看到的网页区域,它是可变化的。</p>
<p>用户可以通过缩放来查看网页内容,当用户放大网页时,我们能看到的网页区域缩小,此时视觉视口变小;同样,当用户缩小网页时,我们能看到的网页区域变大,此时视觉视口也变大;不管放大还是缩小网页,布局视口始终不变。</p>
<p>通过调用<code>window.innerWidth / innerHeight</code> 来获取视觉视口大小。</p>
<h3 id="103-理想视口-ideal-viewport">10.3 理想视口 (ideal viewport)</h3>
<p>理想视口是布局视口的一个理想尺寸。</p>
<p>只有当布局视口的尺寸等于设备屏幕的尺寸时,才是理想视口。</p>
<p>当页面缩放比例为100%时,CSS像素 = 设备独立像素,理想视口 = 视觉视口</p>
<p>通过 <code>window.screen.width/height</code> 获取理想视口大小。</p>
<blockquote>
<p>布局视口限制css 布局,视觉视口控制用户看到的网页内容;<br>
在web浏览器中有两个视口:布局视口,视觉视口;<br>
在移动端,除了布局视口,视觉视口,还有一个理想视口。它是对特定浏览器的布局视口的一个理想尺寸。</p>
</blockquote>
<h2 id="11-移动端适配">11. 移动端适配</h2>
<p>移动设备默认的viewport 是布局视口(layout viewport), 布局视口一般比屏幕大的多,在移动端布局开发时,我们需要的是理想视口(ideal viewport);此时,我们需要引入meta 标签设置viewport。</p>
<p>meta viewport 标签首先是由苹果公司在其safari浏览器中引入的,目的就是解决移动设备的viewport问题。后来各大浏览器厂商也都支持了它。</p>
<p><code><meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;"></code></p>
<p>viewport的配置属性如下:</p>
<table>
<thead>
<tr>
<th>Attribute</th>
<th style="text-align:center">Value</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>width</td>
<td style="text-align:center">正整数或者device-width</td>
<td>以px为单位,定义layout viewport的宽度</td>
</tr>
<tr>
<td>height</td>
<td style="text-align:center">正整数或者device-height</td>
<td>以px为单位,定义layout viewport的高度</td>
</tr>
<tr>
<td>initial-scale</td>
<td style="text-align:center">0.0-10.0</td>
<td>定义页面初始缩放比率</td>
</tr>
<tr>
<td>minimum-scale</td>
<td style="text-align:center">0.0-10.0</td>
<td>定义缩放的最小值;必须小于或等于maximum-scale的值</td>
</tr>
<tr>
<td>maximum-scale</td>
<td style="text-align:center">0.0-10.0</td>
<td>定义缩放的最大值;必须大于或等于minimum-scale的值</td>
</tr>
<tr>
<td>user-scalable</td>
<td style="text-align:center">yes或者no</td>
<td>如果设置为 no,用户将不能放大或缩小网页。默认值为 yes</td>
</tr>
</tbody>
</table>
<p><strong>移动端布局</strong></p>
<p>在移动端,为了让页面布局达到理想效果,需要让布局视口,视觉视口尽可能等于理想视口;</p>
<p>device-width等于理想视口的宽度,所以设置width=device-width就相当于让<strong>布局视口</strong>等于理想视口。</p>
<p>由于initial-scale = 理想视口宽度 / 视觉视口宽度,所以我们设置initial-scale=1;就相当于让<strong>视觉视口</strong>等于理想视口。</p>
<p><strong>页面缩放</strong></p>
<p>当我们设置width时,可以决定布局视口的宽度,但是设置initial-scale也会影响到布局视口宽度,因为布局视口宽度取的是width和视觉视口宽度的最大值,即:<code>布局视口宽度 = Math.max(width, 视觉视口宽度)</code></p>
<p>例如:在iphone中,如果理想视口的宽度是320px,设置width=device-width; initial-scale=2; 则: <code>视觉视口宽度 = 320px/2 = 160px</code>, 布局视口的宽度取width和视觉视口宽度的最大值,故布局视口宽度是400px;如果设置initial-scale=0.5,<code>视觉视口宽度 = 320px * 2 = 640px</code>, 此时布局视口宽度取最大值640px。</p>
<p><em>参考文章</em>:</p>
<p><a href="http://www.conardli.top/blog/article/%E5%A4%9A%E7%AB%AF%E5%BC%80%E5%8F%91/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E9%80%82%E9%85%8D%E6%80%BB%E7%BB%93%EF%BC%88%E4%BA%8C%EF%BC%89%E5%BA%94%E7%94%A8%E7%AF%87.html#%E5%AF%BC%E8%AF%BB">移动端适配总结</a><br>
<a href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Viewport_concepts">视口概念</a><br>
<a href="https://www.jianshu.com/p/7c5fdf90c0ef">viewport、布局视口、视觉视口、理想视口 深入理解</a></p>
<p><em>相关资料</em>:</p>
<p><a href="https://viewportsizes.com/?filter=">手机设备尺寸一览表</a><br>
<a href="https://www.quirksmode.org/mobile/viewports.html">Peter-Paul Koch 博客</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[SVN命令]]></title>
<id>https://feiyayshx.github.io/web-architecture/post/svn-command/</id>
<link href="https://feiyayshx.github.io/web-architecture/post/svn-command/">
</link>
<updated>2020-10-09T05:56:42.000Z</updated>
<summary type="html"><![CDATA[<p>👉svn 日常命令, mac 系统中操作svn命令行。</p>
]]></summary>
<content type="html"><![CDATA[<p>👉svn 日常命令, mac 系统中操作svn命令行。</p>
<!-- more -->
<h3 id="从本地导入代码到服务器">从本地导入代码到服务器</h3>
<pre><code>svn import [本地仓库地址] [服务器地址] --username=[用户名] --password=[密码] -m '注释'
// 例如:
svn import /Users/apple/Documents/eclipse_workspace/weibo svn://localhost/mycode/weibo --username=mj --password=123 -m "初始化导入"
</code></pre>
<h3 id="从服务器检出代码到当前目录">从服务器检出代码到当前目录</h3>
<p><code>svn checkout [svn地址]</code></p>
<p>简写<br>
<code>svn co [svn 项目地址]</code></p>
<h3 id="添加文件到版本库">添加文件到版本库</h3>
<pre><code class="language-javascript">
// 将某个文件添加到版本库
svn add [filePath]
// 例如, src下面的index.js 添加到版本库
svn add src/compiler/index.js
// 添加某个目录下面的所有文件
svn add src
</code></pre>
<h3 id="更新代码到本地">更新代码到本地</h3>
<p>更新到某个版本<br>
<code>svn update -r m [path]</code><br>
简写<br>
<code>svn up</code></p>
<h3 id="提交代码到远程">提交代码到远程</h3>
<p><code>svn commit -m '提交注释'</code></p>
<p>简写<br>
<code>svn ci -m '提交注释'</code></p>
<h3 id="撤销工作区修改的代码">撤销工作区修改的代码</h3>
<p><code>svn revert -R trunk</code></p>
<h3 id="查看文件或目录状态">查看文件或目录状态</h3>
<ul>
<li>
<p>查看目录下的文件及字目录状态,正常状态不显示<br>
<code>svn status [path]</code><br>
【?:不在svn的控制中;M:内容被修改;C:发生冲突;A:预定加入到版本库;K:被锁定;!已经删除的文件,但还未标记为从svn版本库中删除】</p>
</li>
<li>
<p>svn status -v path(显示文件和子目录状态)</p>
</li>
</ul>
<h3 id="查看提交日志">查看提交日志</h3>
<p><code>svn log [path]</code></p>
<h3 id="查看文件详细信息">查看文件详细信息</h3>
<p><code>svn info [path]</code></p>
<h3 id="比较差异">比较差异</h3>
<p><code>svn diff path(将修改的文件与基础版本比较)</code><br>
例如:<code>svn diff test.php</code></p>
<p><code>svn diff -r m:n path(对版本m和版本n比较差异)</code><br>
例如:<code>svn diff -r 200:201 test.php</code></p>
<p>简写:<code>svn di</code></p>
<h3 id="加锁解锁">加锁/解锁</h3>
<p><code>svn lock -m '注释' path</code><br>
例如:svn lock -m “lock test file“ test.php<br>
解锁:svn unlock PATH</p>
<h3 id="删除文件">删除文件</h3>
<p><code>svn delete path -m '注释'</code><br>
例如:<code>svn delete svn://192.168.1.1/pro/domain/test.php -m '删除说明'</code><br>
或者直接<code>svn delete test.js</code>然后再<code>svn ci -m '删除说明'</code>,推荐使用这种<br>
简写:svn (del, remove, rm)</p>
<h3 id="批量删除添加">批量删除/添加</h3>
<p><code>svn status|grep ! |awk '{print $2}'|xargs svn del</code></p>
<p>命令解释:</p>
<ul>
<li>首先执行svn status,查看svn状态,列出文件的所有改动;</li>
<li>!标记的文件是已经删除的文件还未被svn版本库标记;grep !是将!标记的文件单独抽离出来;</li>
<li>再用awk '{print $2}'将抽离出来的文本结果处理,留下每一行的第二段文字,即后面的文件名;<br>
此处必须注意,svn status|grep !和后面的语句|awk '{print $2}'|xargs svn del之间,必须有一个空格,否则终端还是会认为这个!号是特殊符号。</li>
<li>最后递交给svn del命令,使用xargs这个参数构造命令,将每一行的文本作为参数提供给svn del,结果就是所有列出的文件都执行了一遍del了。</li>
</ul>
<blockquote>
<p>批量添加文件同理,只是标记符号是?<code>svn status|grep ? |awk '{print $2}'|xargs svn add</code></p>
</blockquote>
<h3 id="解决冲突">解决冲突</h3>
<p><code>svn resolved [path]</code><br>
注意: 本子命令不会依语法来解决冲突或是移除冲突标记;它只是移除冲突的<br>
相关文件,然后让 PATH 可以再次提交。</p>
<h3 id="忽略">忽略</h3>
<p>https://blog.csdn.net/qq_24909089/article/details/85334570</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Node系列之stream模块]]></title>
<id>https://feiyayshx.github.io/web-architecture/post/node-stream/</id>
<link href="https://feiyayshx.github.io/web-architecture/post/node-stream/">
</link>
<updated>2020-09-28T08:04:25.000Z</updated>
<summary type="html"><![CDATA[<p>👏 前言: 本文是学习Node时,对其中stream模块学习的一个笔记总结,主要是加强理解!</p>
]]></summary>
<content type="html"><![CDATA[<p>👏 前言: 本文是学习Node时,对其中stream模块学习的一个笔记总结,主要是加强理解!</p>
<!-- more -->
<h2 id="1-流的概念">1. 流的概念</h2>
<p>流(stream)是node.js中处理流式数据的抽象接口。</p>
<p>stream模块用于构建实现了流接口的对象。</p>
<p>Node.js 提供了多种流对象,例如,HTTP 服务器的请求和 process.stdout 都是流的实例。</p>
<p>流可以是可读的、可写的、或者可读可写的。 所有的流都是 EventEmitter 的实例。</p>
<h2 id="2-node中四种基本的流类型">2. Node中四种基本的流类型</h2>
<ul>
<li>
<p>Writable - 可写入数据的流(例如 fs.createWriteStream());</p>
</li>
<li>
<p>Readable - 可读取数据的流(例如 fs.createReadStream());</p>
</li>
<li>
<p>Duplex - 可读又可写的流(例如 net.Socket);</p>
</li>
<li>
<p>Transform - 在读写过程中可以修改或转换数据的 Duplex 流(例如 zlib.createDeflate())。</p>
</li>
</ul>
<h2 id="3-流的数据模式">3. 流的数据模式</h2>
<ul>
<li>
<p>二进制模式:Node.js 创建的流都是运作在字符串和 Buffer(或 Uint8Array)上;</p>
</li>
<li>
<p>对象模式(object mode):当创建流时,可以使用 objectMode 选项把流实例切换到对象模式。 将已存在的流切换到对象模式是不安全的</p>
</li>
</ul>
<h2 id="4-可读流的读取模式">4. 可读流的读取模式</h2>
<p><strong>flowing模式(流动模式)</strong>:可读流自动从系统底层读取数据,并通过EventEmitter接口的事件将数据提供给应用;</p>
<p><strong>paused模式(暂停模式)</strong>:必须显式调用stream.read()方法从流中读取数据片段;</p>
<p>所有初始工作模式为paused的Readable流,可以通过三种方式切换到flowing模式:</p>
<ul>
<li>监听data事件</li>
<li>调用stream.resume()</li>
<li>调用stream.pipe()方法将数据发送到Writeable</li>
</ul>
<p>可读流Readable可通过下列方式切换到paused模式:</p>
<ul>
<li>如果不存在管道目标(pipe destination),可以通过调用 stream.pause() 方法实现。</li>
<li>如果存在管道目标,可以通过取消 'data' 事件监听,并调用 stream.unpipe() 方法移除所有管道目标来实现。</li>
</ul>
<h2 id="5-可读流的三种状态">5. 可读流的三种状态</h2>
<p>在任意时刻,可读流应确切处于下面三种状态之一:</p>
<p><code>readable._readableState.flowing = null</code></p>
<p><code>readable._readableState.flowing = false</code></p>
<p><code>readable._readableState.flowing = true</code></p>
<ul>
<li>
<p>若<code>readable._readableState.flowing = null</code>,由于不存在数据消费者,可读流不会产生数据。在此状态下,监听data事件,调用readable.pipe()方法,或者调用readable.resume(),<code>readable._readableState.flowing</code>的值将会变为true, 随着数据生成,可读流开始频繁触发事件。</p>
</li>
<li>
<p>调用<code>readable.pause()</code>,或者<code>readable.unpipe()</code>,或者接收 “背压”(back pressure),将导致 readable._readableState.flowing 值变为 false。这将暂停事件流,但不会暂停数据生成,在这种情况下,为 'data' 事件设置监听函数不会导致 <code>readable._readableState.flowing</code> 变为 true。</p>
</li>
<li>
<p>当 <code>readable._readableState.flowing</code> 值为 false 时, 数据可能堆积到流的内部缓存中。</p>
</li>
</ul>
<h2 id="6-缓冲">6. 缓冲</h2>
<ul>
<li>
<p>可写流和可读流都会在内部的缓冲器中存储数据,可以分别使用的 writable.writableBuffer 或 readable.readableBuffer 来获取。</p>
</li>
<li>
<p>可缓冲的数据大小取决于传入流构造函数的 highWaterMark 选项。 对于普通的流, highWaterMark 指定了字节的总数。 对于对象模式的流, highWaterMark 指定了对象的总数。</p>
</li>
<li>
<p>当调用 stream.push(chunk) 时,数据会被缓冲在可读流中。 如果流的消费者没有调用 stream.read(),则数据会保留在内部队列中直到被消费。</p>
</li>
<li>
<p>一旦内部的可读缓冲的总大小达到 highWaterMark 指定的阈值时,流会暂时停止从底层资源读取数据,直到当前缓冲的数据被消费。</p>
</li>
<li>
<p>当调用 writable.write(chunk) 时,数据会被缓冲在可写流中。 当内部的可写缓冲的总大小小于 highWaterMark 设置的阈值时,调用 writable.write() 会返回 true。 一旦内部缓冲的大小达到或超过 highWaterMark 时,则会返回 false。</p>
</li>
<li>
<p>Duplex 和 Transform 都是可读写的。 在内部,它们都维护了 两个 相互独立的缓冲器用于读和写。 在维持了合理高效的数据流的同时,也使得对于读和写可以独立进行而互不影响。</p>
</li>
<li>
<p>stream API 的关键目标, 尤其对于 stream.pipe() 方法, 就是限制缓冲器数据大小,以达到可接受的程度。这样,对于读写速度不匹配的源头和目标,就不会超出可用的内存大小。</p>
</li>
</ul>
<h2 id="7-自定义可读流">7. 自定义可读流</h2>
<p>为了实现可读流,继承Readable接口并用它构造新对象</p>
<pre><code class="language-javascript">
const { Readable } = require('stream');
class MyRead extends Readable{ // 默认会调用Readable中的read方法
_read(){
// push方法是Readable中提供的, 调用push将结果放入,就可以触发 on('data事件')
this.push('ok');
// 放入null表示这个流没有更多数据了
this.push(null);
}
}
let mr = new MyRead;
mr.on('data',function (data) {
console.log(data);
})
mr.on('end',function () {
console.log('end')
})
mr.on('open',function () {
console.log('open')
})
</code></pre>
<h2 id="8-自定义可写流">8. 自定义可写流</h2>
<p>自定义可写流,继承Writable接口来构造新对象<br>
_write(chunk,encoding,callback)函数:</p>
<ul>
<li>chunk通常是一个buffer,除非我们配置不同的流。</li>
<li>encoding是在特定情况下需要的参数,通常我们可以忽略它。</li>
<li>callback是在完成处理数据块后需要调用的函数。这是写数据成功与否的标志。若要发出故障信号,请用错误对象调用回调函数</li>
</ul>
<pre><code class="language-javascript">
const { Writable } = require('stream')
class MyWrite extends Writable {
_write(chunk, encoding, cb) {
cb();
}
}
let mw = new MyWrite();
mw.write('ok', function () {
console.log('ok')
})
mw.write('ab')
</code></pre>
<h2 id="9-自定义双工流">9. 自定义双工流</h2>
<p>双工流能读又能写,双工流的可读性和可写性操作完全独立于彼此。这仅仅是将两个特性组合成一个对象。</p>
<pre><code class="language-javascript">
const { Duplex } = require('stream');
class MyDuplex extends Duplex{
_read(){
this.push('xxx');
this.push(null)
}
_write(chunk,encoding,cb){
console.log(chunk);
cb() // clearBuffer
}
}
let md = new MyDuplex();
md.on('data',function (chunk) {
console.log(chunk)
})
md.write('ok');
</code></pre>
<h2 id="10-自定义转换流">10. 自定义转换流</h2>
<ul>
<li>
<p>转换流的输出是从输入中计算出来的。</p>
</li>
<li>
<p>对于转换流,我们不必实现read或write的方法,我们只需要实现一个transform方法,将两者结合起来。它有write功能,也可以用它来push数据。</p>
</li>
<li>
<p>可以用于加密、压缩 ,可以把可写流转换成可读流。</p>
</li>
</ul>
<pre><code class="language-javascript">const { Transform } = require('stream');
class MyTransfrom extends Transform {
_transform(chunk, encoding, cb) {
// 这里可以调用push方法
this.push(chunk.toString().toUpperCase()); // this.emit()
cb();
}
}
let my = new MyTransfrom();
process.stdin.on('data',function (chunk) { // 监控用户输入内容
process.stdout.write(chunk);
})
process.stdin.pipe(my).pipe(process.stdout)
// process.std.write('ok'); // console.log() 可写流
</code></pre>
<h2 id="11-管道流">11. 管道流</h2>
<pre><code class="language-javascript">
const stream = require('stream')
var index = 0;
const readable = stream.Readable({
highWaterMark: 2,
read: function () {
process.nextTick(() => {
console.log('push', ++index)
this.push(index+'');
})
}
})
const writable = stream.Writable({
highWaterMark: 2,
write: function (chunk, encoding, next) {
console.log('写入:', chunk.toString())
}
})
readable.pipe(writable);
</code></pre>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Node系列之fs模块]]></title>
<id>https://feiyayshx.github.io/web-architecture/post/node-fs/</id>
<link href="https://feiyayshx.github.io/web-architecture/post/node-fs/">
</link>
<updated>2020-09-28T01:47:56.000Z</updated>
<summary type="html"><![CDATA[<p>👏 fs模块用于与文件系统交互,所有的文件系统操作都具有同步的、回调的、以及基于 promise 的形式。</p>
]]></summary>
<content type="html"><![CDATA[<p>👏 fs模块用于与文件系统交互,所有的文件系统操作都具有同步的、回调的、以及基于 promise 的形式。</p>
<!-- more -->
<h2 id="相关名词">相关名词</h2>
<ul>
<li>文件描述符</li>
</ul>
<h2 id="fs方法">fs方法</h2>
<ul>
<li>fs.copyFile()</li>
<li>fs.access()</li>
</ul>
<h2 id="文件拷贝简单版">文件拷贝(简单版)</h2>
<ul>
<li>fs 操作接受的文件路径可以是字符串、Buffer,或URL对象;</li>
<li>如果读取文件,读到的结果默认是buffer类型;</li>
<li>写入的时候 会清空文件内容,并且以utf8格式类型写入;</li>
</ul>
<pre><code class="language-javascript">
// 将文件homework.js中的内容拷贝到demo.js中
const fs = require('fs')
const path = require('path')
fs.readFile(path.resolve(__dirname,'homework.js'),function(err,data) {
fs.appendFile(path.resolve(__dirname,'demo.js'),data,function(err){
console.log('copy finished')
})
})
</code></pre>
<blockquote>
<p>注意:运行时如果用相对路径,会以process.cwd() 来切换路径,可能会导致不同路径下运行结果不同。故使用绝对路径。</p>
</blockquote>
<p><strong>缺点:</strong></p>
<ul>
<li>读取文件时,读取的内容放在内存中,如果文件过大会浪费内存,淹没可用内存,大型文件不能采用此方式操作;一般64kb 以上的文件实现拷贝尽量不要使用readFile方法。</li>
</ul>
<h2 id="文件拷贝改进版">文件拷贝(改进版)</h2>
<p>简单版的文件拷贝不适合读取大文件,容易造成内存浪费; 对于读取大型文件的场景,需要分段读取文件,实现读取一点写入一点;核心方法有:fs.open()、fs.read()、fs.write()、fs.close()。</p>
<pre><code class="language-javascript">
const fs = require('fs')
function copyFile(source, target, callback) {
const BUFFER_LENGTH = 6
let readPosition = 0
let writePosition = 0
const buffer = Buffer.alloc(BUFFER_LENGTH)
// 打开文件,读取内容
fs.open(source, 'r', function (err, rfd) {
// 打开文件,写入内容
fs.open(target, 'w', function (err, wfd) {
function copy() {
fs.read(rfd, buffer, 0, BUFFER_LENGTH, readPosition, function (err, bytesRead) {
// 读取到的实际字节数
if (err) return callback(err)
if (bytesRead) {
// 将读取的数据写入文件
fs.write(wfd, buffer, 0, bytesRead, writePosition, function (err, written) {
readPosition += bytesRead
writePosition += bytesRead
copy();
})
} else {
fs.close(rfd, () => { })
fs.close(wfd, () => { })
callback()
}
})
}
copy()
})
})
}
/*---------功能测试----------*/
copyFile('./3.fs.js', './demo.js', function () {
console.log('copy finished')
})
</code></pre>
<h2 id="文件拷贝终极版">文件拷贝(终极版)</h2>
<p>文件拷贝的终极版是对改进版的一种优化;fs 基于流封装了文件的可读流方法fs.createReadStream和可写流方法fs.createWriteStream。</p>
<h3 id="fscreatereadstream-可读流内部实现过程及用法">fs.createReadStream 可读流内部实现过程及用法</h3>
<p>实现了stream.Readable接口的对象,将对象数据读取为流数据;</p>
<ul>
<li>
<ol>
<li>内部调用 new ReadStream, 继承于Readable接口;</li>
</ol>
</li>
<li>
<ol start="2">
<li>先进行数据格式化;</li>
</ol>
</li>
<li>
<ol start="3">
<li>默认打开文件,ReadStream.prototype.read</li>
</ol>
</li>
<li>
<ol start="4">
<li>Readable.prototype.read -> ReadStream.prototype._read</li>
</ol>
</li>
</ul>
<blockquote>
<p>想基于Readable接口实现自己的可读流 你需要自己去实现一个_read方法,默认开始读取时会去调用此方法.<br>
可读流对象 必须有on('data') on('end') , 文件流会提供两个方法 open/close<br>
控制读取速率 rs.pause rs.resume</p>
</blockquote>
<h4 id="创建可读流">创建可读流</h4>
<pre><code class="language-javascript">
const fs = require('fs')
const path = require('path')
let readStream = fs.createReadStream(path.resolve(__dirname, 'demo.md'), {
flags: 'r', // 可读性标识,默认值'r',读取文件
encoding: null, // 编码,默认null
autoClose: false, // 读取完毕后自动关闭,默认true
start: 0, // 从文件中指定开始位置读取,包括该位置
end: 4, // 指定读取文件的结束位置,包括该位置
highWaterMark: 2 // 每次读取的字节数,默认64 * 1024 个字节
})
</code></pre>
<h4 id="监听data事件">监听data事件</h4>
<p>流切换到流动模式,数据会被尽可能快的读出</p>
<pre><code class="language-javascript">
let arr = []
// 默认一旦监听data事件,会不停触发data方法
readStream.on('data', function (chunk) {
arr.push(chunk)
})
</code></pre>
<h4 id="监听end事件">监听end事件</h4>
<p>数据读取完毕,触发end事件</p>
<pre><code class="language-javascript">
readStream.on('end', function () {
console.log(Buffer.concat(arr), '文件读取完毕!')
})
</code></pre>
<h4 id="监听error事件">监听error事件</h4>
<pre><code class="language-javascript">
readStream.on('error', function (err) {
console.log(err)
})
</code></pre>
<h4 id="监听open事件">监听open事件</h4>
<pre><code class="language-javascript">
readStream.on('open', function (fd) {
console.log(fd, 'open 打开文件')
})
</code></pre>
<h4 id="监听colse事件">监听colse事件</h4>
<pre><code class="language-javascript">
readStream.on('close',function(){
console.log('close 关闭文件')
})
</code></pre>
<h4 id="设置编码">设置编码</h4>
<p><code>readStream.setEncoding('utf8')</code></p>
<h4 id="暂停和恢复触发data">暂停和恢复触发data</h4>
<pre><code class="language-javascript">
readStream.on('data', function (data) {
readStream.pause(); // 暂停读取
console.log(data);
});
setTimeout(function () {
readStream.resume(); // 读取
},2000);
</code></pre>
<h3 id="fscreatewritestream-可写流用法">fs.createWriteStream 可写流用法</h3>
<p>实现了stream.Writeable 接口对象,将流数据写入到对象中</p>
<h4 id="创建可写流">创建可写流</h4>
<pre><code class="language-javascript">
const fs = require('fs');
const path = require('path');
const ws = fs.createWriteStream(path.resolve(__dirname, 'test.txt'),{
flags:'w',
encoding:'utf8',
autoClose:true,
highWaterMark: 2// 默认写的水位线是16k
});
</code></pre>
<h4 id="write-方法">write() 方法</h4>
<p><code>ws.write(chunk,[encoding],[callback])</code></p>
<ul>
<li>chunk: string/buffer</li>
<li>encoding: 编码格式chunk为字符串时有用,可选</li>
<li>callback: 写入成功后的回调</li>
</ul>
<blockquote>
<p>返回值为布尔值,系统缓存区满时为false,未满时为true</p>
</blockquote>
<h4 id="end-方法">end() 方法</h4>
<p><code>ws.end(chunk,[encoding],[callback])</code><br>
可以在关闭流之前再写入一段数据 如果传入了可选的 callback 函数,它将作为 'finish' 事件的回调函数;</p>
<h4 id="drain-方法">drain() 方法</h4>
<ul>
<li>当流不处在drain状态时,对write()的调用会缓存数据块,返回false;当前所有缓存的数据块都清空后,会触发drain()事件;</li>
</ul>
<pre><code class="language-javascript">
let fs = require('fs');
let ws = fs.createWriteStream('./demo.md',{
flags:'w',
encoding:'utf8',
highWaterMark:3
});
let i = 10;
function write(){
let flag = true;
while(i&&flag){
flag = ws.write("1");
i--;
console.log(flag);
}
}
write();
ws.on('drain',()=>{
console.log("drain");
write();
});
</code></pre>
<h4 id="finish-方法">finish() 方法</h4>
<p>在调用了 stream.end() 方法,且缓冲区数据都已经传给底层系统之后, 'finish' 事件将被触发。</p>
<pre><code class="language-javascript">
for (let i = 0; i < 100; i++) {
ws.write(`hello, ${i}!\n`);
}
ws.end('结束\n');
ws.on('finish', () => {
console.error('所有的写入已经完成!');
});
</code></pre>
<h3 id="借助fs模块提供的可读流与可写流方法实现拷贝">借助fs模块提供的可读流与可写流方法实现拷贝</h3>
<pre><code class="language-javascript">
const fs = require('fs')
let readStream = fs.createReadStream('./3.fs.js', { highWaterMark: 4 })
let writeStream = fs.createWriteStream('./demo.js', { highWaterMark: 2 })
readStream.pipe(writeStream)
</code></pre>
<h2 id="文件可读流的简单实现">文件可读流的简单实现</h2>
<pre><code class="language-javascript">
const fs = require('fs');
const EventEmitter = require('events')
class ReadStream extends EventEmitter {
constructor(path, options = {}) {
super()
this.path = path;
this.flags = options.flags || 'r';
this.encoding = options.encoding || null;
if (typeof options.autoClose == "undefined") {
this.autoClose = true
} else {
this.autoClose = options.autoClose
}
this.start = options.start || 0;
this.end = options.end || undefined;
this.highWaterMark = options.highWaterMark || 64 * 1024;
this.open(); // 默认就调用开启事件
this.offset = this.start; // offset 可以根据每次读取的位置发生变化
this.flowing = false; // 默认就是非流动模式
this.on('newListener', (type) => {
if (type == 'data') { // 说明用户监听了data事件
this.flowing = true;
this.read(); // 读取吧
}
});
}
pipe(ws) {
this.on('data', (chunk) => {
let flag = ws.write(chunk);
if (!flag) {
this.pause();
}
})
ws.on('drain', () => {
this.resume();
})
}
pause() {
this.flowing = false;
}
resume() {
if (!this.flowing) {
this.flowing = true;
this.read(); // 继续读取
}
}
open() {
fs.open(this.path, this.flags, (err, fd) => {
if (err) {
return this.emit('error', err)
}
this.fd = fd; // 将文件描述符保存起来
this.emit('open', fd)
})
}
read() {
if (typeof this.fd != 'number') { // 保证fd 一定存在,不能存在就等会 什么时候存在再用
return this.once('open', () => this.read())
}
// fd 一定存在了, buffer是内存 内存是引用类型
const buffer = Buffer.alloc(this.highWaterMark);
let howMuchToRead = this.end ? Math.min((this.end - this.offset + 1), this.highWaterMark) : this.highWaterMark; // 真正要读取的个数
fs.read(this.fd, buffer, 0, howMuchToRead, this.offset, (err, bytesRead) => { // 真正读取到的个数
if (bytesRead) {
this.offset += bytesRead; // 每次读到后就累加,方便下次继续读取
this.emit('data', buffer.slice(0, bytesRead))
if (this.flowing) {
this.read();
}
} else {
this.emit('end');
if (this.autoClose) {
fs.close(this.fd, () => {
this.emit('close');
})
}
}
})
}
}
module.exports = ReadStream;
fs.createReadStream = function(path, options) {
return new ReadStream(path, options);
};
</code></pre>
<h2 id="文件可写流的简单实现">文件可写流的简单实现</h2>
<ul>
<li>用链表可以实现栈或者队列, 从头部获取数据性能较高;</li>
<li>第一次写入操作是向文件中写入 ,后续的操作都缓存到链表中;</li>
</ul>
<pre><code class="language-javascript">
const EventEmitter = require('events');
const fs = require('fs');
const Queue = require('./Queue');
class WriteStream extends EventEmitter {
constructor(path, options = {}) {
super(options);
this.path = path;
this.flags = options.flags || 'w';
this.encoding = options.encoding || 'utf8';
this.autoClose = options.autoClose || true;
this.highWaterMark = options.highWaterMark || 16 * 1024;
this.open();
// 要判断是第一次写入 还是第二次写入
this.writing = false; // 用来描述当前是否有正在写入的操作
this.len = 0; // 需要的统计的长度
this.needDrain = false;// 默认是否触发drain事件
this.offset = 0; // 每次写入时的偏移量
this.cache = new Queue// 用来实现缓存的
}
write(chunk, encoding = this.encoding, cb = () => { }) { // 用户调用的write方法
// 判断时真的写入 还是放到缓存中
// 用户调用write时 写入的数据可能时stringOrBuffer
chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
this.len += chunk.length;
let ret = this.len < this.highWaterMark;
if (!ret) { // 达到预期或者超过预期 就改变触发drian事件
this.needDrain = true;
}
if (this.writing) { // 如果正在写入就先存起来 稍后再去写入
this.cache.offer({ //f
chunk,
encoding,
cb
})
} else {
this.writing = true;// 标识为正在写入
this._write(chunk, encoding, () => { // z
cb(); // 用户本来的回调要执行
this.clearBuffer();
});
}
return ret
}
clearBuffer() { // 对个异步并发 可以考队列来实现 依次清空
let data = this.cache.poll();
if (data) {
let { chunk, encoding, cb } = data;
this._write(chunk, encoding, () => {
cb();
this.clearBuffer();
})
} else {
this.writing = false;// 缓存中的内容也写入了 清空缓存
if (this.needDrain) {
this.needDrain = false;
this.emit('drain');
}
}
}
open() {
fs.open(this.path, this.flags, (err, fd) => {
if (err) return this.emit('error', err);
this.fd = fd;
this.emit('open', fd);
})
}
_write(chunk, encoding, cb) { // fs.write => doWrite
if (typeof this.fd !== 'number') {
return this.once('open', () => this._write(chunk, encoding, cb))
}
fs.write(this.fd, chunk, 0, chunk.length, this.offset, (err, written) => {
this.len -= written;
this.offset += written;
cb();
})
}
}
module.exports = WriteStream
fs.createWriteStream = function(path, options) {
return new WriteStream(path, options);
};
// 实现读一点 (on('data') on('end') pause resume) 写一点 (write,end)
</code></pre>
<h2 id="手写实现一个链表">手写实现一个链表</h2>
<pre><code class="language-javascript">
class Node {
constructor(element, next) {
this.element = element;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
add(index, element) { // 增加节点
if (arguments.length == 1) {
element = index;
index = this.size
}
if (index < 0 || index > this.size) throw new Error('链表索引异常');
if (index == 0) {
let head = this.head; // 老的头
this.head = new Node(element, head);
} else {
let prevNode = this.getNode(index - 1); // 这里前面节点肯定有,如果没有会走if
prevNode.next = new Node(element, prevNode.next);
}
this.size++;
}
remove(index) { // 删除节点
if(this.size == 0) return null
let oldNode;
if (index == 0) {
oldNode = this.head;
this.head = oldNode && oldNode.next;
} else {
let prevNode = this.getNode(index-1); // 获取当前的前一个节点
oldNode = prevNode.next; // 前一个的下一个就是要删除的