-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
580 lines (273 loc) · 512 KB
/
search.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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>动效技术调查报告(PAG、Lottie)</title>
<link href="/tangBlog/2025/02/12/%E5%8A%A8%E6%95%88%E6%8A%80%E6%9C%AF%E8%B0%83%E6%9F%A5%E6%8A%A5%E5%91%8A%EF%BC%88PAG%E3%80%81Lottie%EF%BC%89/"/>
<url>/tangBlog/2025/02/12/%E5%8A%A8%E6%95%88%E6%8A%80%E6%9C%AF%E8%B0%83%E6%9F%A5%E6%8A%A5%E5%91%8A%EF%BC%88PAG%E3%80%81Lottie%EF%BC%89/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在开发个人网站的过程中发现动效不理想,难以实现复杂动画(主要是因为菜),就开始探索现成的技术解决方案,于是有了该篇文章。<br>本来就想关注手头这个web端的,但是发现有两个较为成熟的多平台方案——Airbnb公司开源项目<code>Lottie</code> 和 腾讯开源的<code>PAG</code>。<br>⚠️不包含任何教程内容,单纯抒发个人见解</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>前端有大致有如下的解决方案,我将其分为了俩大方向,一是以前端程序员代码实现为主,通过JS、CSS、SVG等技术;二是通过设计师相关技术为主,通过AE、Figma等技术工具。<br>篇幅有限,本文主要讨论第二种路线</p><h3 id="代码导向"><a href="#代码导向" class="headerlink" title="代码导向"></a>代码导向</h3><p><strong>1. CSS 动画和 Transitions</strong><br> CSS 动画更适合简单的 UI 交互动画和状态过渡<br> <strong>优势:</strong> 简单易学,性能良好,浏览器原生支持,无需引入额外的库。<br> <strong>劣势:</strong> 功能相对简单,对于复杂的动画效果,CSS 动画可能力不从心,代码量也会增加。维护复杂动画可能不如 JavaScript 库方便。<br><strong>2. JavaScript 动画库 (例如:GSAP, Anime.js, Velocity.js)</strong><br> 按需求采用,通过引入JS库的形式,调用各种API来实现动画效果<br> <strong>优势:</strong> 相比 CSS 动画,JavaScript 动画库通常提供更强大的功能和更灵活的控制,可以创建更复杂的动画效果。<br> <strong>劣势:</strong> 需要引入额外的 JavaScript 库,可能会增加项目体积。学习曲线可能比 CSS 动画稍高。<br><strong>3. SVG 动画</strong><br> 使用 SVG (Scalable Vector Graphics) 格式创建的动画。SVG 动画可以使用 CSS 或 JavaScript 进行控制。<br> <strong>优势:</strong> 矢量图形,无损缩放,文件体积小,适合制作图标动画、线条动画等。<br> <strong>劣势:</strong> 相对来说,SVG 动画的功能不如 Lottie.js 或 JavaScript 动画库强大,对于复杂的动画效果可能比较困难。<br><strong>4. WebGL 动画库 (例如:Three.js, PixiJS)</strong><br> 说白了就性能最高,学习成本也极高,对于一些简单动效需求而言有点杀鸡用牛刀的感觉。<br> <strong>优势:</strong> WebGL 动画库可以利用 GPU 加速,实现高性能的 2D 或 3D 动画效果,适合创建更丰富、更沉浸式的用户体验。<br> <strong>劣势:</strong> 学习曲线较高,需要一定的图形学知识。对于简单的 UI 动画来说,可能过于重量级。</p><h2 id="设计导向"><a href="#设计导向" class="headerlink" title="设计导向"></a>设计导向</h2><p>简单提一下,后文会进行比较<br><strong>1.Lottie</strong><br><strong>2.PAG</strong><br>我个人认为设计导向的路线,对于复杂动效使用AE是最现实的方案,可以发挥较为直接的发挥设计师的能力,主要问题是在效率和兼容性上。</p><h2 id="Lottie-vs-PAG"><a href="#Lottie-vs-PAG" class="headerlink" title="Lottie vs PAG"></a>Lottie vs PAG</h2><p>Airbnb家开源的<a href="https://airbnb.io/lottie/#/README">Lottie</a>,早于腾讯家的<a href="https://pag.io/">PAG | PAG动效</a></p><blockquote><p>题外话,有点佩服国外的互联网公司,查了一下Airbnb公司主要业务是租房子,但是手下有一大把开源项目,外国开源的氛围可见一斑。</p></blockquote><p>这两个技术的Github仓库都有活人在维护,issues也有人在解决,项目状况看着还好,在一两年内应该不会暴毙。<br>PAG应该是相对于Lottie是更加完善的方案,有大量的应用案例为保障(腾讯家国民级产品都在使用),但是我看近期issues,好像在移动端有些bug,但应该能克服。<br>![[Pasted image 20250212225156.png]]<br>这张图大致就反映二者的主要区别,具体可以看这份技术报告<a href="https://pag.io/docs/pag-lvs.html">China LiveVideoStackCon2022 · PAG官网 | PAG动效</a>当然这只是腾讯的一家之言,我还没有具体试验过,姑且当对,等后续我实操过后会更一篇实际体验报告</p><h3 id="谈谈我的看法"><a href="#谈谈我的看法" class="headerlink" title="谈谈我的看法"></a>谈谈我的看法</h3><p><strong>围绕这张图来分享下我调查到的:</strong></p><h4 id="文件格式"><a href="#文件格式" class="headerlink" title="文件格式"></a>文件格式</h4><p>PAG有自己的特殊文件格式,而Lottie其实也有叫 <code>.lottie</code>的标准文件。具体文件大小我没测试过,待定,但是<code>.json</code>文件可以通过官方的一个网站<a href="https://lottiefiles.com/tools/lottie-to-dotlottie?utm_source=dotlottieio">Convert Lottie JSON to dotLottie - Free Online Converter - LottieFiles</a>转化成 <code>.lottie</code>,lottie官网说能压缩近1/10大小,并带来一些特有的性能提升,具体可以看<a href="https://lottiefiles.github.io/lottie-docs/">Home - Lottie Docs</a>。<br>也就是Lottie没腾讯说的那么不堪,PAG的特有格式带来了体积小的优势,而Lottie则可以使用更通用的JSON格式也可以转化成更高效的dotLottie格式。<br>俩家估计就是在争<strong>标准格式</strong>的定义权。</p><h4 id="平台支持"><a href="#平台支持" class="headerlink" title="平台支持"></a>平台支持</h4><p>PAG最新版在移动端有崩溃风险,但应该不久能解决。PAG<br>Lottie其实也是适配所有主流平台的,Android、Apple(ios、macOS、VisionOS、tvOS)、Web、Windows,此外还适配了React、Vue、Svelte甚至是RN,我觉得最有趣的是可以和Framer这个动画框架联动。</p><h4 id="AE特性支持"><a href="#AE特性支持" class="headerlink" title="AE特性支持"></a>AE特性支持</h4><p>PAG全支持,Lottie部分支持(但是基本功能够用,没那么不堪)<br>但这点确实是事实,Lottie也承认在不同平台有些AE功能无法适配。<br>这点上PAG全面胜出。</p><h4 id="工具支持"><a href="#工具支持" class="headerlink" title="工具支持"></a>工具支持</h4><p>这一点是这张图里没有提到到,工具支持主要是利好设计师。<br>PAG和Lottie都主要围绕adobe家的After Effect这款工具,但是除此之外Lottie的生态应该远远优于PAG。<br>Lottie适配Figma、Canvas…….<br>![[Pasted image 20250212233820.png]]<br>生态比PAG好了不只是一星半点,具体可以看<a href="https://lottiefiles.com/integrations">Integrations and Plugins to Simplify Motion Design Workflow - LottieFiles</a>太夸张了,感觉打通了整个工具流的上下游。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>其是讨论这么多,究竟采用哪个,我自己心里也没有定数,或许这就是技术的魅力吧,就如同React、Vue、Svelte各有各存在的理由。</p><h2 id="主要参考资料"><a href="#主要参考资料" class="headerlink" title="主要参考资料"></a>主要参考资料</h2><p><a href="https://pag.io/">PAG官网 | PAG动效</a><br><a href="https://github.com/Tencent/libpag">Tencent/libpag: The official rendering library for PAG (Portable Animated Graphics) files that renders After Effects animations natively across multiple platforms.</a><br><a href="https://lottiefiles.com/">LottieFiles: Download Free lightweight animations for website & apps.</a><br><a href="https://airbnb.io/lottie/#/README">Lottie</a><br><a href="https://juejin.cn/post/6966866737396449316?searchId=202502122104233D2F066BC96FD0E50996">飞书中 Lottie 动画的应用本文主要介绍 Lottie 动画方案、使用方法、与 Gif 动画的性能测试对比,以及在飞 - 掘金</a><br><a href="https://juejin.cn/post/7220698356775567417?searchId=202502122140504D310A9C0320A3F2B28E">微信都在用的开源动效方案【PAG动效】当设计给的动画越来越复杂, 还原度要求越来越高的情况下,就需要微信都在用的开源动效 - 掘金</a><br><a href="https://juejin.cn/post/7220375870228185146?searchId=202502122140504D310A9C0320A3F2B28E">PAG 动效方案使用总结近几年能明显感觉到,互联网产品已经越来越离不开动效了:不管是APP里会动的加载动画UI,还是直播 - 掘金</a></p>]]></content>
</entry>
<entry>
<title>我最近在干什么【2】</title>
<link href="/tangBlog/2025/02/06/%E6%88%91%E6%9C%80%E8%BF%91%E5%9C%A8%E5%B9%B2%E4%BB%80%E4%B9%88%E3%80%902%E3%80%91/"/>
<url>/tangBlog/2025/02/06/%E6%88%91%E6%9C%80%E8%BF%91%E5%9C%A8%E5%B9%B2%E4%BB%80%E4%B9%88%E3%80%902%E3%80%91/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这系列的上一篇是2024.12.05写的,现在是2025.02.06,这都两个月🤔小久。<br>不是完整全面的技术分享,话题聚焦<strong>个人成长(学的技术、了解到的信息、看的书……)</strong> 方面。文章偏意识流点,单纯分享<strong>我最近在干什么</strong>,不定期更新,有话则长无话则短。</p><h2 id="学Node-js"><a href="#学Node-js" class="headerlink" title="学Node.js"></a>学Node.js</h2><p>12月感觉就在磨磨唧唧的学Node.js,同时穿插其其中是各种乱七八糟的一些非专业课考试。Node.js只能说是学了一点点,感觉真就只有一点点,果真上大学影响学习。<br>现在闲下来感觉还是得学Node,也大概是下学期的一个主要发展点🎯吧。</p><h2 id="准备期末考"><a href="#准备期末考" class="headerlink" title="准备期末考"></a>准备期末考</h2><p>总不能真挂科吧,临时抱佛脚临时抱佛脚🤗🦶,真就只有期末周能逼出全力,OOP、DSA、数字逻辑都是考前一天晚上发力救起来的,概统提前四五天开始预习,很幸运嗷,也是有惊无险的都过了。<br>大学里绝大部分专业课就是能花20%的时间干出80%的效果,而剩下20%的东西却要投入80%的时间去卷,对我而言在这些事情上投入过度确实是得不偿失,不如临阵磨枪来的高效。</p><h2 id="修官网"><a href="#修官网" class="headerlink" title="修官网"></a>修官网</h2><p>期末考完后,就又重新回到正轨上去了,开始写项目找找感觉。都是些静态的东西,目前都不需要和后端连桥。<br>修了修muxi官网的一些bugs,主要是样式上的问题。原项目使用的是Sass,感觉Sass给CSS带来的都是好处,可以无痛衔接。我还同时移用了下在Tailwind中学习到的响应式布局给官网的页面都添加上了响应式,直接用Sass写起来小难受的。<br>响应式布局写起来还是挺有味道的,巧妙的地方就是去思考在功能逻辑不变的情况下,通过改变布局逻辑来适配不同的屏幕尺寸和操作方式。特别是桌面端和移动端适用同一套业务的情况下,响应式可以极大的复用代码。</p><h2 id="个人网站"><a href="#个人网站" class="headerlink" title="个人网站"></a>个人网站</h2><p>其实这一学期就一直在念叨着要写个人网站,就等着这个假期来腾出时间和精力去投入,这也就是我最近一直在干的事情。<br>主要也是一次对自己这学期技术积累的综合考验,也微微进行了一点提升,使用<code>Next.js + TailwindCSS + TypeScript + Shadcn + Framer</code>,想着走一整套设计开发、部署上线的流程。当然也开发过程中也是遇到了各种问题,也是学到了很多东西。<br>之所以使用,Next.js这个框架,当初主要是抱着学习的心态去的,像是SSR、SSG、ISR这些对我而言比较新颖的渲染方式。但是实际上手后,发现了那些一些内置的好处,确实给开发带来的一定的便利。利明显大于弊,好吧也可能是我还没具体感受到啥弊端吧🫡。<br>没有设计只能自己上手,也是挺考验自己的思维的,要到<a href="https://www.pinterest.com/">Pinterest</a>上去找灵感,还要自己去思考配色(好丑,没天赋)。在开发过程中也发现,Tailwind在设置自定义颜色这块还是不够便利的,可能是我的个人喜好原因,我会为某一种颜色使用官方的那种的风格从 <code>XXX100</code> 配置到 <code>XXX1000</code>,也就是意味着要手动设置10次,用起来是相当麻烦。我在思考有啥更好的解决方法,感觉是值得研究一下的。</p><h2 id="过年放假"><a href="#过年放假" class="headerlink" title="过年放假"></a>过年放假</h2><p>过年这几天,踏踏实实的爽摆了10天😋。<br>闲着的时候,磨磨唧唧地看完了一本黑塞的《在轮下》,这本算是黑塞的早期成名作了,能从中感受到一股少年意气,这点和之后所作的《悉达多》、《德米安》有着截然不同的风格。结合黑塞的人生去看,《在轮下》就是他自己二十年来人生经历的缩影,年少成名、中途辍学、抑郁寻短、脱下长衫、荒唐离世……黑塞在书写自己的青年人生,探讨着头角峥嵘之辈是如何在老旧的体制下,要么失去棱角泯于众人,要么被斥为异端远离世人。<br>阅读这书过程中体悟最深的句子反倒来自于书封,“时代的巨轮呼啸飞驰,怎样的人生才算精彩?谁也没有答案。只知道,我们要加速奔跑。”虽然我感觉这句话和本书的本意没有任何瓜葛,但是很符合如今这个时代,也巧妙的给书名做了注。在轮下,个体在时代的巨轮下如何生活,本书是悲剧结尾,黑塞没有给出自己的答案,我也给不了答案🫡。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>今天开始半开工了,写一篇文章收收心,继续上路喽,慢慢把手头的事情一件件落实落地。</p>]]></content>
<tags>
<tag> 杂谈 </tag>
</tags>
</entry>
<entry>
<title>2024年末总结</title>
<link href="/tangBlog/2025/01/01/2024%E5%B9%B4%E6%9C%AB%E6%80%BB%E7%BB%93/"/>
<url>/tangBlog/2025/01/01/2024%E5%B9%B4%E6%9C%AB%E6%80%BB%E7%BB%93/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>回顾一下我2024年的主要成长点,主要是想记录一下我走过的路。<br>这篇也是文笔水平有限想到啥写啥,纯纯意识流🥸。</p><h2 id="人生首个挫折🚧"><a href="#人生首个挫折🚧" class="headerlink" title="人生首个挫折🚧"></a>人生首个挫折🚧</h2><p>对,我将它视为我人生中第一个真正意义上的挫折,它真的把我打的措手不及,对我未来人生规划彻彻底底的颠覆。虽然在之前的文章中我从未袒露,现实中我也尽量避开,但我还是想在这篇文章中坦然的讲讲吧。这件事情一直是我心上的一道深深的伤疤,很少向别人提起。大一上的我其实一直准备着转专业去当一名教师,计划去摆烂的过一生的,也不能这么说,就是一直幻想是在高中讲台上和学生讲讲课唠唠嗑,安安心心的过日子。计算机其实一直是我的planB或者更准确地说是众多兴趣爱好中的一个,那时候的我对计算机就业是没有任何心理准备的。<br>期间准备转专业的过程不想多言,我努力过,但最后的结果就是转专业失败。我恨在我通过了校内的笔试面试,却卡在了政府的官员身上,这种令人无可奈何的戏剧转折打的我措手不及。我也是从这才懂得努力是不一定会成功的,过往的人生我一直都算是幸运儿,只要努力就能得到正反馈,我也一直觉得会一直心想事成下去,但很遗憾人生就不是这样的,只是我还没遇到罢了。虽然我多想笑着来说,觉得已经看淡,现在突然回忆起还是难免有点愤慨。我得知教育局因为财政原因没通过转专业合同的事情,觉得人生是有多荒诞啊,任何人可以拿任何理由去决定你的人生。<br>我能走出这段挫折,我不会去傻呵呵的感谢磨难,我不会去宽恕陌生人给我的飞来横祸,但确实如尼采所说“杀不死我的,只会使我更强大。”我能走出来,全是因为我拯救了自己。我得承认我自己的顽强,在那段彻底黑暗的日子里还能不停的走下去。<br>虽然现在说来就这么几段话,但我用了一整个大一下的时间去消化😋。</p><h2 id="技术转型🎭"><a href="#技术转型🎭" class="headerlink" title="技术转型🎭"></a>技术转型🎭</h2><p>在大一学年,我是在学习安卓开发方面的技术,准确地说,我实际上真正有在学习技术的时间段是在大一上,大一下我在人生规划的路线上陷入了混乱,不知道该干什么,不知我该学什么技术才适合我,我甚至也在考虑我到底应不应该放弃学习开发,转而向保研读研这条路,我那时候也确实把重心放在了卷学分绩、卷竞赛上去了。我并不是否认这段时间,我这学期的很多成果都得益于这段混乱时光的摸索,现在人生的宽度,选择的自由都是来自于那时候的迷茫。大一下更多是在为我这一学期铺垫,但也必须承认大一下的这段混乱的时间,对目前我专注的前端可以说是没有什么直接的帮助,可以算是丢失了一整个大一的时间。<br>在大一的暑假,也就是今年的七八月份,离开了学校,回到了家里, 我才开始有精力去回味我大一下这一学期,我也才慢慢的转变了路线,决定选择就业方向。但具体什么方向,真正的决定其实还是返校前一周才决定的,这之间也有近两月的摇摆期,前端后端还是继续移动应用开发,应该学习什么技术栈?这些问题的答案对那时的我来说还都是模糊不清的。<br>很难三言两语讲清楚曾经的我是怎么思考的,那时候的我也确实是优柔寡断的,很多时候都是卡在起步,犹犹豫豫的不知道该往哪里走,更多的是来自于迷茫,来自于对自己和未来的不确定,也让我错失了许多机会。但最终在某一次因为我过于考虑面子犹犹豫豫丢了机会后,我才晓得现在的我还不配面子和里子都拿,先得学会放下面子去拿里子,等拿到里子后面子自然会来的。<br>九月开学,我开始全身心投入前端学习,努力去填补大一的缺少。安卓转前端可以算是自费武功,重新开始吧,几乎是没有任何类似的经验可以依靠。但现在一学期走下来,我认为这个技术转型的决定是绝对正确的,绝对有意义的。有了这一次走出舒适区的经历后,对我自己的能力有了认知,做出走出舒适区的决定是痛苦,但带来的成长也是巨大的。这期间我所学习到的不单单是前端技术,而且是重新塑造了我的三观,成为我灵魂的一部分了。<br>本规划的是一路学到RN走跨端开发,计划总是不断随着我眼界的改变而改变,也可以从我往期的文章中开出一些端倪,现在计划是往全栈靠靠。或许这个目标在未来也会改变,但目前总的来看还是不断前进的。</p><h2 id="想清楚的有价值的问题💡"><a href="#想清楚的有价值的问题💡" class="headerlink" title="想清楚的有价值的问题💡"></a>想清楚的有价值的问题💡</h2><p>现在回想起来这一年确实好长好长啊,我都不敢相信一年我能变化这么多。<br>我就碎碎念一些自以为是的大道理了。</p><h3 id="以德报德,以直报怨"><a href="#以德报德,以直报怨" class="headerlink" title="以德报德,以直报怨"></a>以德报德,以直报怨</h3><p>虽然想避开转专业失败这件事,但这就是今年绕不开的主题,之所以是对我如此非凡,因为我对结局确实无能为力,我只能改变自己。对于久在书斋的我来说,身旁多是老师同学,过完人生中遇到的多是善人,误以为世界上多是此般。但社会上就是会有人不会如你所愿,他们不会真正地善待你,只是将你当成棋子或杂草。我不能说这种人很多,但起码我真正遇到了,虽然我时常忘记,但我现在回想起来了。我偶然会突然想起人心险恶,尽管常常忘记。我希望以后的我能真正学会对陌生人多点戒心,对不怀好意之人敢动狠心,对真心相待之人回报真心。</p><h3 id="面子不值钱"><a href="#面子不值钱" class="headerlink" title="面子不值钱"></a>面子不值钱</h3><p>上段也提到过我知道了要放下面子,其实我觉得这一直是我人生成长的一个主题,小学初中高中,我就是不断地在某些事情中学会面子没那么重要,一点点地学会丢脸,但每每回想都还做地不够,现在我其实也还是没有彻底懂得丢脸,还没有能力去彻底丢脸,是的,我认为丢脸也是需要能力的。<br>我今年看到过一句话,是李嘉诚说的,<strong>“人的面子是最不值钱的东西,当你放下面子去挣钱的时候,说明你已经懂事了;当你可以用钱挣回面子的时候,说明你已经成功了;当你可以用面子去挣钱的时候,说明你已经是个人物了。但如果你一天到晚无所事事,只在乎所谓的面子的时候,那就说明你一辈子就这样了。”</strong> 面子和里子,我现在都还没有能用面子赚钱的能力,希望明年的我能更加懂得丢脸。</p><h3 id="追求真正幸福"><a href="#追求真正幸福" class="headerlink" title="追求真正幸福"></a>追求真正幸福</h3><p>我今年才正式从唯成绩论的高中思维里摆脱出来,以前其实也是为了追求幸福的,但是学生时代嘛,将 <code>成绩好 === 幸福</code> 过久了。以往人生都是建立在做出成绩了就等于得到幸福,但到了大学这不一样了。今年经过老登的分享,让我又重新意思到了,我上半年过的这么拧巴,而下半年又感觉重新找回自我的转折点,那就是在于重回幸福本身。<br>为什么不用快乐这词,因为在我这<code>幸福 !== 快乐</code>,每个对幸福都有自己的定义,我不想去争辩什么。<br>人活着就是为了幸福,学生是为了幸福去追求学业成绩,但如果学业成绩给你带不来你想要的幸福就没必要死抓不放,毕竟人除了学校的垃圾课外并非一无所有,工作上也是如此,给不了幸福就得想着破局、想着去改变,回归幸福本身。</p><h3 id="想多都是问题,做多都是答案"><a href="#想多都是问题,做多都是答案" class="headerlink" title="想多都是问题,做多都是答案"></a>想多都是问题,做多都是答案</h3><p>这句话是我今年在一次班会分享中听到的,在当时的语境中是想督促我们多做事情,但我现在想挪用一下这句话,我来为它重新作注。<br><strong>想多都是问题,做多都是答案</strong>,我认为最基础的成长循环就是发现问题->寻找解决答案->出现新问题->寻求新答案,当然这模型是非常非常基础的,只是想说明<strong>问题</strong>和<strong>答案</strong>二者缺一不可。如何高效的运转起来,如何顺应时代的变化,如何适应个人的变化······我今年刻意去构建工作流的日子算起以及未来,我都将围绕这俩个基点展开,不断去完善<strong>想问题找答案</strong>这个cycle。<br>想问题很重要,我今年和别人多次提起过,看到问题这件事情本身就是非常有意义的,你能看见别人看不见的,你就是体现出个人的眼界、认知水平在这一件事情上比别人高。而如何提升眼界、认知水平,我个人目前探索出的答案就是搭建<strong>信息流</strong>,我会在之后的part说到,其实我寻找对这套信息流有了更深刻的认知,还有改进的空间,但碍于精力有限一直没去完善。<br>做事情也很重要,我现在很难给出一个确切的答案,因为我其实也很混乱,还没形成一套完整的工作流,但我现在能给出的一个模糊的关键词就是<strong>快</strong>。任何事情都先做起来,有想法有点子就记录下来,有产品想法就先做个demo,有技能知识要学就fast learn。虽然这好很粗糙,但就如我开始提到的,做事情去寻找答案是立足于发现问题,完善方案、迭代模型这是问题阶段思考,做事情这一阶段就是去做着寻找该问题的答案的。</p><h3 id="做自己的产品经理"><a href="#做自己的产品经理" class="headerlink" title="做自己的产品经理"></a>做自己的产品经理</h3><p>这一点是我这学期在思考AI时代,我这种可怜又卑微的码农该如何适应而得出的阶段性结论。<br>人工智能,我认为其发展的根本目的就是为了代替人工,现在互联网寒业就是不断的从技术密集型下降到劳动密集型,而AI又提升了个体的生产效率(当然前提是会充分运用工具),岗位在需求不变的情况是一定是不断溃缩的。<br>算力这种新质生产力渐渐会通过各种形式流入各行各业取代掉人力的。但目前还好,AI还只能是copilot,还得是有人类去作为主驾驶员的,这就是AI暂时给人类留有的生产劳动岗位了。<br>这个主驾驶位具体是什么,各行业都有相应的位置,而对产品开发而言就是产品经理。其实这点说小点就是学会有产品意识,有自己的鬼点子且试着去将想法落地,说大点就是自己学会当老总,当leader,而你管理的不是具体的人,而是不同的AI工具。这也是我这学期和将来计划不断去进步的方向。<br>AI从本质上看就是算力的一种表现形式,是先进生产力的代表,是走向共产的必由之路。我也在今年略微看到了乌托邦的幻影了,当然过程必然是曲折的,但人就是要不断的从落后的劳动中解放出来,从机械性的重复劳动中解放出来,投入到艺术性的创造劳动中去,无论你是否愿意。</p><h2 id="干成的有意义的事情🎉"><a href="#干成的有意义的事情🎉" class="headerlink" title="干成的有意义的事情🎉"></a>干成的有意义的事情🎉</h2><h3 id="信息流"><a href="#信息流" class="headerlink" title="信息流"></a>信息流</h3><p>今年暑假干的最有意义的事情就是初步搭建起了信息流,我在下半年也在一点点的完善它,但现在有了新的认识,还有有新的大变动,这具体的实现细节我不太想在这篇多说,等我寒假迭代完这套信息流之后或许会写一篇分享,或许也不会。可以从我<a href="https://drunksweet.github.io/tangBlog/2024/10/24/Obsidian%E4%B8%AA%E4%BA%BA%E7%9F%A5%E8%AF%86%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7%E4%BD%93%E9%AA%8C%E5%88%86%E4%BA%AB/">Obsidian个人知识管理工具体验分享 | 焦糖酒的妙妙屋</a>这一篇中窥见一些端倪。简单的说就是<code>信息搜集-信息记录-信息输出-信息回顾</code>这个flow,每个人品味不同具体用啥工具口味也不同,但搭建信息流都是要追求高效的。</p><h3 id="个人博客"><a href="#个人博客" class="headerlink" title="个人博客"></a>个人博客</h3><p>害,其实这篇估计也是没啥人看的,但我相信时间的力量,留给将来的人考古吧,我还是认为这是挺浪漫的一件事。<br>这一点算是上一点信息流中信息输出的一part,输出的时间是必不可少的,因为这步涉及了信息的理解和整合,在写博客的整个流程对我而言是高效吸收内化知识的一个活动,整体上看博客的水平也是在不断进步的,我将来也不会停止。<br>未来或许会探索更多的形式,以及探索如何将其更好的融入信息流中。</p><h3 id="技术广度"><a href="#技术广度" class="headerlink" title="技术广度"></a>技术广度</h3><p>特别是前端技术学习,没啥好说的,是我这学期精力的主要宣泄点,fast learn的实操之处。模糊掉技术细节,我最真切的感受就是任何事情学到一定程度都是有瓶颈的,而它的破局之道是在它所忽视的地方。<br>计算机技术在很多方面都是互补的,给技术细分概念是为了更好的理解它们,而学习计算机就在理解它们的不同的同时能够去找到它们的共通点,去把握那个key,那个脉。当然这我这么说并不是说我已经找到了,但这是我目前学习技术的一个指导思想。而学习技术最本质的目的还是为了追求幸福(微微call back一下),具体点就是提升个人的能力,拔高思维的境界。</p><h3 id="社团会长"><a href="#社团会长" class="headerlink" title="社团会长"></a>社团会长</h3><p>这点其实我当初是奔着保研加分去的,但现在而言也不失一个突破舒适区的一件事情。在这位置干的事情更多的是social,去认识人类的多样性,去锻炼自己的社交能力。</p><h2 id="明年计划📝"><a href="#明年计划📝" class="headerlink" title="明年计划📝"></a>明年计划📝</h2><p>其实很难说出具体的todolist,我也就只能笼统的去定一下模糊的目标了,关键词还是<strong>变</strong>,目标是找到份牛马实习吧🥲。</p><ul><li>多去社交,和不曾认识的人社交,和不愿沟通的人社交,和不敢接触的人社交。</li><li>多去了解不同领域的知识,不拘泥于计算机。</li><li>多去将想法落地。</li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>其实还有应该还有挺多点没写到,既然没想起来,说明没那么重要,那也就没啥必要写了。<br>洋洋洒洒写下来还是能感觉到自己在往理想的方向成长的,遗憾难免,希望尚存。<br>期待明年我的变化🤗。</p>]]></content>
</entry>
<entry>
<title>Radmin LAN技术分析——NAT穿透</title>
<link href="/tangBlog/2024/12/16/Radmin-LAN%E6%8A%80%E6%9C%AF%E5%88%86%E6%9E%90%E2%80%94%E2%80%94NAT%E7%A9%BF%E9%80%8F/"/>
<url>/tangBlog/2024/12/16/Radmin-LAN%E6%8A%80%E6%9C%AF%E5%88%86%E6%9E%90%E2%80%94%E2%80%94NAT%E7%A9%BF%E9%80%8F/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文主要是分析一下Radmin LAN,最近在我们团队要做Radmin LAN的产品体验,我负责技术分析部分。本文不涉及具体操作教程,聚焦于工作原理。<br><strong><a href="https://www.radmin-lan.cn/">Radmin LAN</a></strong> 特别适合用于远程工作、在线游戏或搭建小型虚拟局域网。它不同于传统的企业级 VPN 服务,配置简单且使用方便。Radmin LAN 是由一家创始于1999年的(11-50人规模)的小公司Famatech,专注于桌面和服务器管理远程控制软件的开发。<br><strong>Radmin LAN</strong> 实际上就是一个<strong>VPN</strong>,主要用于连接不同地理位置的设备,让它们仿佛在同一局域网内,官网自己也叫它 <strong><a href="https://www.radmin-vpn.com/cn/">Radmin VPN </a><strong>。之后我会将这两种叫法混叫,当侧重在虚拟局域网时,我会叫它</strong>Radmin LAN</strong>,而侧重不同设备通信时则叫<strong>Radmin VPN</strong>。</p><h2 id="Radmin-LAN-的工作原理"><a href="#Radmin-LAN-的工作原理" class="headerlink" title="Radmin LAN 的工作原理"></a><strong>Radmin LAN 的工作原理</strong></h2><p>Radmin LAN 的工作原理核心在于<strong>通过其云服务器</strong>搭建起<strong>分布式虚拟局域网</strong>,让分布在不同地理位置的设备能够像在同一局域网中一样通信。这种原理通常涉及几个关键步骤:</p><ul><li>创建<strong>虚拟局域网(LAN)</strong></li><li>建立<strong>点对点(P2P)连接</strong></li><li>为每个设备分配一个虚拟 IP 地址以<strong>模拟局域网</strong></li><li>相互间通信使用<strong>数据加密</strong>和<strong>隧道传输</strong></li></ul><hr><h3 id="创建或加入虚拟网络🌐"><a href="#创建或加入虚拟网络🌐" class="headerlink" title="创建或加入虚拟网络🌐"></a><strong>创建或加入虚拟网络🌐</strong></h3><p>用户配置 Radmin VPN 网络,用户在 Radmin VPN 客户端中选择 “创建网络”,设置网络名称和密码。此操作会<strong>生成一个虚拟局域网(LAN)</strong>,允许其他设备通过网络名称和密码加入。其他设备选择 “加入网络”。 输入由网络创建者提供的名称和密码即可<strong>加入同一个虚拟局域网</strong>。</p><h3 id="建立点对点(P2P)连接🔗"><a href="#建立点对点(P2P)连接🔗" class="headerlink" title="建立点对点(P2P)连接🔗"></a><strong>建立点对点(P2P)连接🔗</strong></h3><p>处于不同内网的设备建立起P2P连接的,这是本文分析的重点。<br>实际上Radmin LAN是使用NAT穿透技术,那啥是NAT穿透?</p><h4 id="内外网🧱"><a href="#内外网🧱" class="headerlink" title="内外网🧱"></a>内外网🧱</h4><p>我们先理解一下内外网相关的知识。<br><strong>外网IP是唯一的</strong>,而<strong>不同内网之间的内网IP</strong>是可以一样。内外网之间的分界就是路由器,路由器将作为一个网关,一边连接着外网,一边连接着内网中的不同设备。<br>我们先介绍一下NAT(Network Address Translation,网络地址转换),这是<strong>路由器</strong>常用的一种技术,用于将<strong>内网设备的私有 IP 地址</strong>映射为<strong>公网的 IP 地址</strong>,从而实现多个设备共享一个公网 IP。<br>同一局域网即一个内网中的设备并不直接和外网即互联网通讯,而是通过一个统一的路由器连接到外网上,可以理解为我们在一个家庭里和外部的联系是靠一个专门的<strong>外交官</strong>🧑💼,路由器就是这个外交官,他来负责我们对外的通信。由路由器统一代表我们对外进行通信。<br>如何涉及到两个局域网之间通信就得通过路由器(外交官)进行了,我们无法之间将两个局域网联系在一起,因为你想嘛,如果两个局域网是直接连在一起的,那和一个局域网有什么区别,就好比你直接把对门的邻居和之间的楼道的都买下来了,一层楼都是你家的就不分彼此了。<br>路由器他同时负责记录到底是谁要对外通信即,将我们的内网IP映射成一个端口,外网则不需要知道你到底是谁(内网IP),而需要和路由器进行通信。端口可以理解为一个代号,就像一个家庭中不太可能就只有一个家庭成员,例如一个家庭中由【妈妈,爸爸,女儿,儿子】,【妈妈,女儿】同时要对外通信📨,路由器(外交官A🧑💼)对外通信时就会对找到对应的另一个外交官B🧑💼说我代表家庭A的一号,而不是说是妈妈,因为别不应该知道。同时路由器又找到女儿想要找到那个人的外交官C🧑💼,告诉他说我代表家庭A的二号。<br>而内网的之间的通信,因为在同一局域网内,所以可以之间通信,就像在同一间屋子里,有啥事情吼一声就好,不需要外交官进行协调。<br>既然理解了内外网的区别,那就让我们继续看看如何实现两个不同局域网内设备的通讯问题的。</p><h4 id="两个不同局域网之间的设备互相访问"><a href="#两个不同局域网之间的设备互相访问" class="headerlink" title="两个不同局域网之间的设备互相访问"></a>两个不同局域网之间的设备互相访问</h4><p>两个不同局域网之间的设备互相访问,可以通过以下方法实现,具体取决于网络环境和访问需求:</p><ul><li>直接公网访问</li><li>使用 VPN(虚拟专用网络)</li><li>NAT 穿透(P2P 连接)</li><li>使用中继服务器<br>我们来看看为什么我们平时无法直接将两个设备联系在一起👇</li></ul><h5 id="直接公网访问"><a href="#直接公网访问" class="headerlink" title="直接公网访问"></a>直接公网访问</h5><p>我们在啥都没配置的情况下就是直接使用方法一的即直接公网访问,然而这需要我们访问的对方有公网 IP,也就是我们能找到对方的外交官,还要能成功的完成<strong>端口映射</strong>。<br>继续上文中的比喻,我们的的外交官A能处理好NAT,家庭A的儿子想和对面家庭B的儿子玩,但是他不知道家庭B儿子实际的代号,只能乱写个代号或者空着,外交官A🧑💼拿着我们的信找到要找的外交官B🧑💼(这里实际简化过程,走公网的话,实际上是外交官A将信交给公网的外交官帮忙传达下信息中,实际过程上要跳好几下的),说“我代表家庭A的4号成员,这封信要给你们家的XX”,但是对面不知道我们说的是谁,即<strong>端口映射未配置正确</strong>,对面外交官就把信笑嘻嘻的收下,然后转头就把信扔了,反之亦然。<br>所以想要通过公网互相访问,双方就得都配置好<strong>端口映射</strong>,这不是本文讨论的重点,我就简单比喻一下,就像是家庭A告诉外交官A🧑💼,以后谁要是找<strong>8080</strong>号,实际上就是找儿子,找8081,实际上就是找女儿,对面也是如此。<br>Radmin LAN实际是将下面三种方法的综合运用,剩下三个在本文中就不单独拎出来细分了。</p><h4 id="Radmin-LAN的云服务器协调"><a href="#Radmin-LAN的云服务器协调" class="headerlink" title="Radmin LAN的云服务器协调"></a>Radmin LAN的云服务器协调</h4><p>Radmin LAN在外网上有服务器负责初步协调不同局域网设备之间的连接,云服务器帮助设备发现彼此的公网 IP 地址,并尝试通过 <strong>点对点(P2P)</strong> 的方式直接建立连接。<br>用回上文的比喻,两个家庭AB的儿子A和儿子B想一起玩,就都排出自己外交官A和B,他们都实现了NAT。而Radmin提供了个<strong>和事佬</strong>🥸,不同家庭的外交官先找到这个Radmin和事佬,让他来帮助我们斡旋,帮两个外交官对接好两个儿子的代号,专业上称<strong>UDP打洞</strong>。<br>同时对接完事后还让两个想要通信的外交官能碰面,如果协商的顺畅就可以直接握手,两家之间拉一个电话线☎️📞,之后之间打电话聊天(建立P2P连接),不用每次都要外交官去跑腿。这就是传说中的NAT穿透。<br>但是也有可能遇到些意外导致协商不成,即遇到<strong>NAT 阻碍</strong>,那么Radmin中继服务器这个和事佬就负责在两家间跑腿,帮忙转发信息。<br>这其实是避开了真正的公网,因为Radmin LAN服务器像是专职的和事佬,专门负责使用Radmin LAN的外交官们,能避开目标设备所在的局域网没有公网 IP(静态或动态)或在目标局域网的路由器上配置了 <strong>端口映射</strong>(Port Forwarding)。<br>通过这种方式就实现了将不同局域网联系在一起的效果。</p><h3 id="模拟局域网🌐"><a href="#模拟局域网🌐" class="headerlink" title="模拟局域网🌐"></a><strong>模拟局域网🌐</strong></h3><p>之后Radmin LAN就回给每个加入网络的设备会被分配一个<strong>虚拟 IP 地址</strong>,这就是之前讲到过的内网IP,相当于给连了电话的不同家庭,让它们又由一个外交官统一管理,实现搭建虚拟局域网的效果,这个虚拟 IP 是设备在虚拟局域网内的唯一标识。</p><h3 id="数据加密和隧道传输🕳️"><a href="#数据加密和隧道传输🕳️" class="headerlink" title="数据加密和隧道传输🕳️"></a><strong>数据加密和隧道传输🕳️</strong></h3><p>Radmin LAN 或者说是 Radmin VPN,创建一个加密的虚拟隧道。所有通过隧道传输的数据都会被加密,确保通信的安全性。 数据加密不仅防止被第三方窃取,还保证了用户的隐私安全。<br>加密就不是本文讨论的重点了,感兴趣可以自己去了解一下VPN加密</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h3><p>Radmin VPN 的工作原理可以总结为以下几点:</p><ol><li><strong>初始连接</strong>:<ul><li>通过 Radmin 的云服务器完成设备发现和连接协调。</li></ul></li><li><strong>点对点通信</strong>:<ul><li>优先建立 P2P 连接,提高速度和效率。</li></ul></li><li><strong>加密通信</strong>:<ul><li>数据通过加密隧道传输,保护用户数据安全。</li></ul></li><li><strong>虚拟局域网</strong>:<ul><li>通过虚拟 IP 地址模拟一个局域网环境,让不同设备可以像在同一局域网中一样操作。</li></ul></li></ol><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>这篇文章由于采用了比喻,降低理解难度的时候难免会过滤掉一些实际上的技术细节,但也有效的减低了准入门槛 ,适合非专业的读者阅读,也希望能让对实际技术感兴趣的读者有所收获。<br>我本人也是边学边写,感觉计算机网络确实好神奇,有什么错误的地方,欢迎指正。</p>]]></content>
<tags>
<tag> 计算机网络 </tag>
</tags>
</entry>
<entry>
<title>我最近在干什么【1】</title>
<link href="/tangBlog/2024/12/05/%E6%88%91%E6%9C%80%E8%BF%91%E5%9C%A8%E5%B9%B2%E4%BB%80%E4%B9%88%E3%80%901%E3%80%91/"/>
<url>/tangBlog/2024/12/05/%E6%88%91%E6%9C%80%E8%BF%91%E5%9C%A8%E5%B9%B2%E4%BB%80%E4%B9%88%E3%80%901%E3%80%91/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>打算开一个新系列,偏休闲点的,不是完整全面的技术分享,话题还是聚焦<strong>个人成长(学的技术、了解到的信息、看的书……)</strong> 方面。文章偏意识流点,单纯分享<strong>我最近在干什么</strong>,不定期更新,有话则长无话则短。</p><h2 id="JS精进"><a href="#JS精进" class="headerlink" title="JS精进"></a>JS精进</h2><p>之前为了快速从安卓转型到前端,趁早开始干项目实操,就主打速通,两个月fast learn了前端三件套和React,学得挺粗糙笼统的,没有多少内部整合的时间。<strong>计划一轮学How,先用起来;二轮学Why,学学原理机制;三轮学What,读读源码。</strong><br>一个月前(11月初)本计划开始学习RN,走走跨端路线,但也没想到这么快就碰到瓶颈,RN起了个头就推进不下去了。于是乎就把精力重新集中会JS的学习,学了近两周,补了之前略过的知识点和ES6之后的语法糖,查漏补缺的同时学学了闭包、提升、类型转化、GC等等稍微偏底层的知识。</p><h2 id="前端工程化"><a href="#前端工程化" class="headerlink" title="前端工程化"></a>前端工程化</h2><p>其实这件事情应该是贯彻始终的,我也是从最开始的一脸懵逼,啥是vite,为啥不用CRA(create-react-app),ESlint是啥,npm又是啥?当初给我的感觉就是混乱,什么乱七八糟的东西,差生文件多。学习过程中也是东学一点西学一点,不知不觉间凑齐了大致上的拼图,拼成了前端工程化这个包罗万象的概念。<br>目前或详细或粗糙的了解一下有负责<strong>代码规范</strong>的Linter(ESLint、Prettier、Stylelint)、<strong>代码管理</strong>的Git(插一嘴,最近看到篇文章在讲monorepo感觉也挺有意思的)、负责<strong>包管理</strong>的npm、pnpm啥的、代码测试的Vitest、Jest(我不碰,目前用不到)还有Moudule Bundler(VIte、Webpack)。<br>前三周左右为了写分享,就去了解算是上述工具中最熟悉的Git,本以为一两篇文章就能把底层机制讲好,但没想到我之前用的不过是冰山一角。洋洋洒洒准备了一周写了一周,才大致上完结了Git系列,现在回头看发现其实还是有很多不足,特别是从第三章开始,以后有机会在修改吧。<br>上周也是看了眼Vite的文档,也是感觉到我之前用的Vite竟然背后还有这么多有趣的机制,像是TreeShake、HMR、代码分割和压缩。但这些东西对目前的我来说还是远远用不到的。更底层的机制我还不配去碰。<br>挺有意思的,也是一步一脚印,慢慢的将这上面这些眼花缭乱的名词变成了具体的概念。</p><h2 id="青训营"><a href="#青训营" class="headerlink" title="青训营"></a>青训营</h2><p>11月也在打卡青训营,确实有些东西,学到不少前沿的东西。</p><h2 id="React精进"><a href="#React精进" class="headerlink" title="React精进"></a>React精进</h2><p>这周嘛才正式开始React的精进之路,也是没想到在Props和State这块知识能卡我这么久,这还是ing形式,走一步看一步。</p><h2 id="无服务器、边缘计算、全栈化"><a href="#无服务器、边缘计算、全栈化" class="headerlink" title="无服务器、边缘计算、全栈化"></a>无服务器、边缘计算、全栈化</h2><p>这两天看到的几个有趣的文章中我仿仿佛佛get到了一些前端的趋势,有些玄妙的技术变化变得合理起来了。<br>大致上就是Vercle之类的云计算公司在推动边缘无服务器,React和Next也在默默向全栈发展(React Server Components),SSR、SSG等渲染技术也逐渐流行,总之整体上的大趋势就是前端全栈化,不断大前端。<br>提一嘴,两个月前刚开始学Tailwind的时候犹豫好几天,在考虑css技术的最优选,也傻呵呵的写了篇现在看来很稚嫩的文章,但此时,我只能说我的直觉是对的,是符合大趋势的,utility-css的方向是正确的。<br>也是坚定了我寒假学习Astro的心,看看有没有时间吧,有的话也推一推NodeJS的进度。<br>现在给我的感觉就是<strong>知道的越多,就越发知道我的无知</strong>,有太多东西想要去学去了解,这一路上一定是充满惊喜和挑战的,我也因此愈发期待明天的到来了。<br>技术的更新迭代对现在我的来说是机遇,代表着行业的洗牌,跟不上节奏自会淘汰。</p><h2 id="生产而不是消费"><a href="#生产而不是消费" class="headerlink" title="生产而不是消费"></a>生产而不是消费</h2><p>我不知道是不是这么翻译嗷,这句话我是从外网一文章中看到的,大致就是在说“<strong>讨论2024年该学什么框架、学什么技术是没有意义的,大部分作者和读者不过是在消费这情绪,而不是转身投入到生产实践中。</strong>”<br>虽然我不敢苟同这观点,技术选型时的犹豫与思考是有意义的,人总不可能选择早就被时代置否的技术吧,但是<strong>转身投入到生产实践中</strong>是我赞成的。想多了都是问题,做多了都是答案,技术学习过程中就应该是“实践-思考-实践”的不断上升螺旋。<strong>在实践中发掘思考的基点,思维在给行动指引前行的方向。</strong></p><h2 id="阅读"><a href="#阅读" class="headerlink" title="阅读"></a>阅读</h2><p>最近这两周也是开始重新阅读了,人不能永远淤在技术中。黑塞的书总能给我当下的生活一些启示。高考刚毕业时读的《德米安》从中看到了我的影子,而上周读的《悉达多》又让我看到了我性格的另一面。<strong>好好用全身心去感受当下,哪有什么遥远的未来,向现在取经,现在就是未来。</strong><br>思加缪情书集》也挺有意思,之前读过他的其他一些书,像是《局外人》、《西西弗神话》,在我印象中加缪一直是个冷冰冰的哲人形象,冷静又假装热情的去看世界这个巨大的草台班子。但在这书中我看到了另一个加缪,他充满热情,骨子里溢出浪漫的气质,他也会为一个人焦虑,等待思慕之人的消息,在夜里辗转反侧。也看到他用颤抖的手写下一个又一个浪漫到让人心酸的话。<strong>期待,我在期待,那最后为什么成了一直的等待呢。</strong> 我与加缪共情同感,我钦佩他能勇敢的写下那些文字,失去自我的同时又保持着自我。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>其实挺有东西可写的,思绪不断的飘荡,跟着感觉码下自己声音,让文字去喝止脑海中的回响。<br>2024.12.05</p>]]></content>
<tags>
<tag> 杂谈 </tag>
</tags>
</entry>
<entry>
<title>JS特性之Hoisting(提升)</title>
<link href="/tangBlog/2024/11/24/JS%E7%89%B9%E6%80%A7%E4%B9%8BHoisting%EF%BC%88%E6%8F%90%E5%8D%87%EF%BC%89/"/>
<url>/tangBlog/2024/11/24/JS%E7%89%B9%E6%80%A7%E4%B9%8BHoisting%EF%BC%88%E6%8F%90%E5%8D%87%EF%BC%89/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>为了写这篇文章其实也不容易,本来想讲GC的,发现lkq写过了,后来想讲类型转换,发现🗻🐟🏠写过了。<br>🫡🫡🫡<br>最后没办法了,让我水一水喽(bushi)</p><hr><p>补一下,现在又学习了些新知识,发现这篇讲的有些错误,以补充或纠正的形式特此改正。(24/11/27)</p><h2 id="Hoisting提升🏗️前置知识"><a href="#Hoisting提升🏗️前置知识" class="headerlink" title="Hoisting提升🏗️前置知识"></a>Hoisting提升🏗️前置知识</h2><h3 id="啥是提升?"><a href="#啥是提升?" class="headerlink" title="啥是提升?"></a>啥是提升?</h3><p>简单的说 Hoisting 是JS引擎在代码执行前将<strong>所有声明</strong>提升到其<strong>所在作用域的顶部</strong><br>这句话你可以在看完这篇文章后再回头看看这句话,真的是一言蔽之。<br>接下来我会简单讲解一下。</p><h3 id="所有声明"><a href="#所有声明" class="headerlink" title="所有声明"></a>所有声明</h3><p>JS中拢共有 </p><ul><li>变常量<code>var let const</code></li><li>函数<code>function</code></li><li>类<code>class</code></li><li>模块<code>import export</code><br>是的这些都会提升,但是JS引擎在处理这些声明时会有不同的细节处理。</li></ul><h3 id="所在作用域"><a href="#所在作用域" class="headerlink" title="所在作用域"></a>所在作用域</h3><p>JS中拢共 <code>global function block module</code>四个常规的作用域,这不会区别自己学去。<br>[[JS作用域]]<br>好像还有词法作用域 (Lexical Scope)、动态作用域 (运行时上下文context)、私有作用域 (Private Scope),嘿🫠我也不会,估计是不同的领域了,下次学到的时候补上😁😁😁。</p><blockquote><p><strong>补充</strong><br>lexical scope 主要是和闭包closure有关系的概念,我在[[JS精进之Closure (闭包)]]这篇文章中会讲到<br>context运行时上下文,我目前的理解是包含了lexical scope(此外还有变量环境variable environment、this绑定),lexical是静态的,context是函数运行时的上下文,和函数栈有关,是动态的。<br>即context会根据lexical产生的词法作用域链、变量环境的信息、this绑定的信息形成具体的运行上下文<br>私有作用域目前我没学到🫠<br>类class有关的相关知识我也没完整的学习过,就不在这里乱讲了。</p></blockquote><p>记住<strong>Hoisting提升</strong>只会提升到其<strong>所在作用域的顶部</strong>,不会发生将function scope内的提升到global scope的事情。</p><h2 id="详细讲解"><a href="#详细讲解" class="headerlink" title="详细讲解"></a>详细讲解</h2><h3 id="var-的提升"><a href="#var-的提升" class="headerlink" title="var 的提升"></a><code>var</code> 的提升</h3><p>用 <code>var</code> 声明的变量会被提升到作用域顶部,但仅提升声明部分,<strong>初始化(赋值)不会提升</strong>。<br>提一句var会全局污染,即会绑定到window上,不了解的可以自己去查资料看<code>var、let、const</code>的区别.<br><strong>例子:</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// undefined</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">5</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure><h2 id="底层执行过程:1-JavaScript-引擎将-var-a-提升到作用域顶部,等同于:-2-因此,在变量赋值之前,a-的值是-undefined。"><a href="#底层执行过程:1-JavaScript-引擎将-var-a-提升到作用域顶部,等同于:-2-因此,在变量赋值之前,a-的值是-undefined。" class="headerlink" title="底层执行过程:1. JavaScript 引擎将 var a 提升到作用域顶部,等同于: 2. 因此,在变量赋值之前,a 的值是 undefined。"></a><strong>底层执行过程:</strong><br>1. JavaScript 引擎将 <code>var a</code> 提升到作用域顶部,等同于:<br> <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// undefined</span></span><br><span class="line">a = <span class="number">5</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure><br>2. 因此,在变量赋值之前,<code>a</code> 的值是 <code>undefined</code>。</h2><h3 id="let-和-const-的提升"><a href="#let-和-const-的提升" class="headerlink" title="[[let 和 const 的提升]]"></a>[[let 和 const 的提升]]</h3><p><code>let</code> 和 <code>const</code> 也会被提升,但它们的变量在提升时会被放入一个称为 <strong>暂时性死区(Temporal Dead Zone, TDZ)</strong> 的区域,只有在声明之后才能访问。</p><p><strong>例子:</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b); <span class="comment">// ReferenceError: Cannot access 'b' before initialization</span></span><br><span class="line"><span class="keyword">let</span> b = <span class="number">10</span>;</span><br></pre></td></tr></table></figure><p><strong>底层执行过程:</strong></p><ol><li><code>let b</code> 被提升到作用域顶部,但在声明之前无法访问。</li><li>由于在 <code>let b</code> 之前尝试访问变量 <code>b</code>,引擎抛出 <code>ReferenceError</code>。</li></ol><h4 id="啥是-暂时性死区(Temporal-Dead-Zone-TDZ)"><a href="#啥是-暂时性死区(Temporal-Dead-Zone-TDZ)" class="headerlink" title="啥是[[暂时性死区(Temporal Dead Zone, TDZ)]]"></a>啥是[[暂时性死区(Temporal Dead Zone, TDZ)]]</h4><p><strong>暂时性死区</strong>是 JavaScript 中一种行为:<br>在作用域内,虽然变量已经“被提升”(即解析时已经被声明),但在实际声明和初始化完成之前,访问该变量会抛出 <code>ReferenceError</code> 错误。<br>这是为了避免变量在声明之前被意外访问或使用,增加代码的可预测性和安全性。<br>有死区和没死区粗暴的看就是,一个爆<code>ReferenceError</code>,一个爆<code>undefined</code></p><hr><h3 id="函数的-Hoisting"><a href="#函数的-Hoisting" class="headerlink" title="函数的 Hoisting"></a><strong>函数的 Hoisting</strong></h3><h2 id="函数又和前面的机制不同,函数声明的提升会将整个函数提升🫡🫡🫡,而函数表达式则只是会将变量提升,不提升函数。-1-函数声明的提升⭐函数声明会被完整地提升到作用域顶部,因此在声明之前也可以调用函数。例子:底层执行过程:1-函数声明-function-greet-被完整提升到作用域顶部,等同于:"><a href="#函数又和前面的机制不同,函数声明的提升会将整个函数提升🫡🫡🫡,而函数表达式则只是会将变量提升,不提升函数。-1-函数声明的提升⭐函数声明会被完整地提升到作用域顶部,因此在声明之前也可以调用函数。例子:底层执行过程:1-函数声明-function-greet-被完整提升到作用域顶部,等同于:" class="headerlink" title="函数又和前面的机制不同,函数声明的提升会将整个函数提升🫡🫡🫡,而函数表达式则只是会将变量提升,不提升函数。#### 1. 函数声明的提升⭐函数声明会被完整地提升到作用域顶部,因此在声明之前也可以调用函数。例子:底层执行过程:1. 函数声明 function greet() { ... } 被完整提升到作用域顶部,等同于: "></a>函数又和前面的机制不同,函数声明的提升会将整个函数提升🫡🫡🫡,而函数表达式则只是会将变量提升,不提升函数。<br>#### 1. <strong>函数声明的提升</strong><br>⭐函数声明会被<strong>完整地提升</strong>到作用域顶部,因此在声明之前也可以调用函数。<br><strong>例子:</strong><br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">greet</span>(); <span class="comment">// Hello!</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">greet</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"Hello!"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br><strong>底层执行过程:</strong><br>1. 函数声明 <code>function greet() { ... }</code> 被<strong>完整提升</strong>到作用域顶部,等同于:<br> <figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">greet</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"Hello!"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="title function_">greet</span>(); <span class="comment">// Hello!</span></span><br></pre></td></tr></table></figure></h2><h4 id="2-函数表达式的提升"><a href="#2-函数表达式的提升" class="headerlink" title="2. 函数表达式的提升"></a>2. <strong>函数表达式的提升</strong></h4><p>函数表达式(包括箭头函数)不会提升其赋值部分,<strong>仅变量名会被提升</strong>。<br><strong>例子:</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo); <span class="comment">// undefined</span></span><br><span class="line"><span class="keyword">var</span> foo = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"Hello!"</span>);</span><br><span class="line">};</span><br><span class="line"><span class="title function_">foo</span>(); <span class="comment">// Hello!</span></span><br></pre></td></tr></table></figure><p><strong>底层执行过程:</strong></p><ol><li>只有 <code>var foo</code> 被提升,赋值部分不会提升,等同于:<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> foo;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(foo); <span class="comment">// undefined</span></span><br><span class="line">foo = <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"Hello!"</span>);</span><br><span class="line">};</span><br><span class="line"><span class="title function_">foo</span>(); <span class="comment">// Hello!</span></span><br></pre></td></tr></table></figure></li></ol><p>使用 <code>let</code> 或 <code>const</code> 声明函数表达式时,会触发暂时性死区:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(bar); <span class="comment">// ReferenceError</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">bar</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"Hi!"</span>);</span><br><span class="line">};</span><br></pre></td></tr></table></figure><hr><h3 id="class-类声明"><a href="#class-类声明" class="headerlink" title="class (类声明)"></a><strong><code>class</code> (类声明)</strong></h3><p>类声明也会被提升,但同样会存在暂时性死区,在声明之前无法访问。<br><strong>示例</strong>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> instance = <span class="keyword">new</span> <span class="title class_">MyClass</span>(); <span class="comment">// ReferenceError报错,MyClass 在 TDZ 中</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">name</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><hr><h3 id="import-模块声明"><a href="#import-模块声明" class="headerlink" title="import (模块声明)"></a><code>import</code> (模块声明)</h3><p><code>import</code> 总是在模块顶部执行,不能在代码块中使用。<br><strong>示例</strong>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { myFunc } <span class="keyword">from</span> <span class="string">'./module.js'</span>;</span><br><span class="line"><span class="title function_">myFunc</span>();</span><br></pre></td></tr></table></figure><hr><h3 id="export-模块声明"><a href="#export-模块声明" class="headerlink" title="export (模块声明)"></a><strong><code>export</code> (模块声明)</strong></h3><h2 id="特点:-用于导出变量、函数、类等,使其可以在其他模块中使用。-支持命名导出(export-)和默认导出(export-default)。示例:"><a href="#特点:-用于导出变量、函数、类等,使其可以在其他模块中使用。-支持命名导出(export-)和默认导出(export-default)。示例:" class="headerlink" title="特点:- 用于导出变量、函数、类等,使其可以在其他模块中使用。- 支持命名导出(export {})和默认导出(export default)。示例:"></a><strong>特点</strong>:<br>- 用于导出变量、函数、类等,使其可以在其他模块中使用。<br>- 支持命名导出(<code>export {}</code>)和默认导出(<code>export default</code>)。<br><strong>示例</strong>:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> myVar = <span class="number">42</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">myFunc</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"Hello!"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></h2><h3 id="对比总结:"><a href="#对比总结:" class="headerlink" title="对比总结:"></a>对比总结:</h3><table><thead><tr><th>特性</th><th><code>var</code></th><th><code>let</code></th><th><code>const</code></th><th><code>function</code></th><th><code>class</code></th><th><code>import/export</code></th></tr></thead><tbody><tr><td><strong>作用域</strong></td><td>\</td><td>\</td><td>\</td><td>函数作用域</td><td>块作用域</td><td>模块作用域</td></tr><tr><td><strong>变量提升</strong></td><td>是(值为 <code>undefined</code>)</td><td>是(TDZ限制)</td><td>是(TDZ限制)</td><td>是</td><td>是(TDZ限制)</td><td>是</td></tr><tr><td><strong>重复声明</strong></td><td>可以</td><td></td><td>不可以</td><td>不适用</td><td>不适用</td><td>不适用</td></tr><tr><td><strong>可重新赋值</strong></td><td>是</td><td>是</td><td>否</td><td>不适用</td><td>不适用</td><td>否</td></tr></tbody></table><h3 id="Hoisting提升的先后顺序"><a href="#Hoisting提升的先后顺序" class="headerlink" title="[[Hoisting提升的先后顺序]]"></a>[[Hoisting提升的先后顺序]]</h3><p>看到这不知道你有没有疑惑,所有声明都会发生提升,那究竟谁比谁更能提升🫡🫡🫡,总不能左脚🦶踩右脚🦶升天不是。<br>在 JavaScript 中,不同类型的声明(如 <code>var</code>、<code>let</code>、<code>const</code>、<code>function</code>、<code>class</code>、<code>import/export</code>)它们的<strong>执行优先级</strong>和提升行为有所不同。以下是提升的<strong>先后顺序</strong>及具体规则:</p><h4 id="提升顺序的原则"><a href="#提升顺序的原则" class="headerlink" title="提升顺序的原则"></a><strong>提升顺序的原则</strong></h4><ol><li><strong>模块系统优先</strong>:<code>import</code> 和 <code>export</code> 的静态绑定首先被处理。</li><li><strong>函数声明优先</strong>:在代码执行前,函数声明会被提升到所在作用域的顶部。</li><li><strong>变量声明依次提升</strong>:<code>var</code> 早于 <code>let</code> 和 <code>const</code>,但值初始化按代码顺序进行。</li><li><strong><code>class</code> 的提升特殊</strong>:类声明会被提升,但无法在声明前使用(存在 TDZ)。</li></ol><h4 id="提升优先级"><a href="#提升优先级" class="headerlink" title="提升优先级"></a><strong>提升优先级</strong></h4><table><thead><tr><th>类型</th><th>提升顺序</th><th>初始化可用性</th><th>是否受 TDZ 限制</th><th>特性解释</th></tr></thead><tbody><tr><td><strong><code>import</code></strong></td><td>最高</td><td>在代码执行前绑定</td><td>是</td><td>模块加载的静态绑定,解析阶段完成,但无法访问</td></tr><tr><td><strong><code>function</code></strong></td><td>第二</td><td>在声明前可调用</td><td>否</td><td>函数声明整体提升,初始化为可调用函数</td></tr><tr><td><strong><code>var</code></strong></td><td>第三</td><td>在声明前可用(<code>undefined</code>)</td><td>否</td><td>声明提升但初始化在原始位置</td></tr><tr><td><strong><code>let</code> 和 <code>const</code></strong></td><td>第四</td><td>声明前不可用</td><td>是</td><td>存在 TDZ,初始化需等到执行到声明语句才有效</td></tr><tr><td><strong><code>class</code></strong></td><td>最后</td><td>声明前不可用</td><td>是</td><td>类声明会提升,但使用前需显式定义</td></tr></tbody></table><ul><li><strong>最高优先级</strong>:<code>import/export</code> 静态绑定最先解析。</li><li><strong>中间优先级</strong>:<code>function</code> 声明高于 <code>var</code>。</li><li><strong>最低优先级</strong>:<code>let</code>、<code>const</code> 和 <code>class</code>,它们都受 TDZ 限制。</li></ul><h4 id="综合示例:优先级的对比"><a href="#综合示例:优先级的对比" class="headerlink" title="综合示例:优先级的对比"></a><strong>综合示例:优先级的对比</strong></h4><p>以下代码展示了多种声明的提升顺序和执行规则:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">func</span>()); <span class="comment">// "I am a function!"</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(x); <span class="comment">// undefined</span></span><br><span class="line"><span class="comment">// console.log(y); // 报错:Cannot access 'y' before initialization</span></span><br><span class="line"><span class="comment">// console.log(z); // 报错:Cannot access 'z' before initialization</span></span><br><span class="line"><span class="comment">// console.log(MyClass); // 报错:Cannot access 'MyClass' before initialization</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">func</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"I am a function!"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> x = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">let</span> y = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">const</span> z = <span class="number">30</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = <span class="string">"Class Example"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>执行顺序</strong>:</p><ol><li>函数 <code>func</code> 提升并初始化。</li><li><code>var x</code> 声明提升但值为 <code>undefined</code>。</li><li><code>let y</code> 和 <code>const z</code> 提升但进入 TDZ,未初始化。</li><li><code>class MyClass</code> 提升但进入 TDZ,未初始化。</li></ol><h2 id="为什么要提升?"><a href="#为什么要提升?" class="headerlink" title="为什么要提升?"></a>为什么要提升?</h2><p>这也是我当初了解到提升这一逆天特性时的最大疑惑,吃饱了撑着么要这么干?<br>我自己也解释不清,欧克,下面让我们看看GPT是怎么说的:</p><blockquote><p>提升(Hoisting)是 JavaScript 的设计特性之一,它从一开始就融入了语言的实现中。设计提升的原因和历史背景与 JavaScript 的初衷、执行模型、开发环境的需求,以及语言设计的权衡密切相关。</p><ol><li><strong>简化解析和作用域解析</strong>,提升性能。</li><li><strong>支持灵活的代码结构</strong>,适合快速开发和调试。</li><li><strong>向下兼容</strong>,保留早期代码的行为一致性。</li><li><strong>模仿 C 语言的声明模型</strong>,降低学习成本。</li><li><strong>宽容开发者错误</strong>,降低入门门槛。</li></ol></blockquote><p>我们来挑重点看,主要就是俩点,一是<strong>能提高编译器效率</strong>,二是设计者就是想要<strong>易用</strong>🫡(毕竟是10天就完成了第一个版本,原则上就是降低入门成本,减少语法报错的可能性,不管结果死活了,能跑就是赢)</p><h3 id="那么为啥Hoisting能提高编译效率呢?"><a href="#那么为啥Hoisting能提高编译效率呢?" class="headerlink" title="那么为啥Hoisting能提高编译效率呢?"></a>那么为啥Hoisting能提高编译效率呢?</h3><p>在 JavaScript 执行的早期阶段,解析器会对整个代码进行作用域解析。将变量和函数的声明“提升”到作用域的顶部,可以使解析器在代码运行之前快速定位所有变量和函数,从而优化代码执行过程。如果没有提升,解析器必须动态检查代码块中是否存在变量,这会显著增加解析器的复杂性和性能开销。</p><h3 id="那么为什么不把这逆天机制改了呢?"><a href="#那么为什么不把这逆天机制改了呢?" class="headerlink" title="那么为什么不把这逆天机制改了呢?"></a>那么为什么不把这逆天机制改了呢?</h3><p>还是有请GPT:</p><blockquote><p><strong>1. 向下兼容性</strong><br>JavaScript 已经被广泛应用,去掉提升会破坏大量现有代码的运行。因此,ECMAScript 保留了提升特性,同时通过引入 <code>let</code> 和 <code>const</code> 来改进。<br><strong>2. 保留历史遗留行为</strong><br>JavaScript 的早期设计中考虑了宽松的语法规则以适应快速开发环境,提升就是这些规则的遗留产物。删除提升会导致历史代码中的行为发生改变,难以维护。<br><strong>3. 提升的替代方案更复杂</strong><br>没有提升的语言通常需要更严格的声明规则(如 Python),这与 JavaScript 的宽松设计理念相悖。虽然提升容易被滥用,但它为开发提供了一种低门槛的动态方式。</p></blockquote><p>行,我起码是信了,但我觉得历史原因是更多的。Git也是如此,即使现在SHA-1已经不安全了,但还是因为历史原因未用SHA-256去替换。</p><h2 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h2><h3 id="如何避免提升的陷阱?"><a href="#如何避免提升的陷阱?" class="headerlink" title="如何避免提升的陷阱?"></a>如何避免提升的陷阱?</h3><ol><li><strong>使用 <code>let</code> 和 <code>const</code></strong><br>它们也会被提升,但由于存在 <strong>暂时性死区(TDZ)</strong>,未初始化前访问会抛出 <code>ReferenceError</code>,从而避免了意外行为。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(x); <span class="comment">// ReferenceError</span></span><br><span class="line"><span class="keyword">let</span> x = <span class="number">10</span>;</span><br></pre></td></tr></table></figure></li><li><strong>严格模式</strong><br>使用 <code>strict mode</code>,避免变量未声明时被使用:<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a); <span class="comment">// ReferenceError</span></span><br><span class="line">a = <span class="number">5</span>;</span><br></pre></td></tr></table></figure></li><li><strong>遵循“先声明,后使用”规则</strong><br>避免依赖提升,让代码更加直观。</li></ol><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>呼,结束了,但感觉有开了个坑,<strong>作用域解析机制</strong>、**[[JS作用域]]** 或许有的一讲🫡,下次在说吧。</p><hr><p>没想到这篇这么杂,有种风暴中心旁的混乱感,感觉还得在精进一波才能像庖丁解牛般讲Hoisting彻底讲清楚。</p>]]></content>
<tags>
<tag> JS </tag>
</tags>
</entry>
<entry>
<title>终端应用开发沉思录</title>
<link href="/tangBlog/2024/11/12/%E7%BB%88%E7%AB%AF%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E6%B2%89%E6%80%9D%E5%BD%95/"/>
<url>/tangBlog/2024/11/12/%E7%BB%88%E7%AB%AF%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E6%B2%89%E6%80%9D%E5%BD%95/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>以下所有分析皆是从我的视角出发,探讨下我现行局势下觉得最有可能的实现且有未来发展前景的技术方案。由于本人没有啥开发经验,所以多是纸上谈兵,仅仅记录和分享下我个人想法。</p><h2 id="移动App的开发模式:"><a href="#移动App的开发模式:" class="headerlink" title="移动App的开发模式:"></a>移动App的开发模式:</h2><p>在技术选型上,其实好久没这么犹豫过了,最近几天学到React Native,但迟迟没有全身心投入,就是在疑虑其和市面上的其他技术相比是否值得学习。<br>目前移动应用开发有以下三条主要道路(原生 H5 混合)外加一个国内特色的小程序</p><table><thead><tr><th><strong>开发模式</strong></th><th><strong>典型应用场景</strong></th><th><strong>优点</strong></th><th><strong>缺点</strong></th><th><strong>代表技术</strong></th></tr></thead><tbody><tr><td>原生开发</td><td>高性能需求、UI/UX要求严格的场景</td><td>性能高,完整API支持,用户体验佳</td><td>开发和维护成本高,无法跨平台</td><td>Android SDK, iOS SDK, UWP</td></tr><tr><td>H5开发</td><td>跨平台、快速迭代、内容动态更新的场景</td><td>一次开发,多平台适配,更新简单</td><td>性能受限,功能受限</td><td>HTML, CSS, JavaScript, JSCore, V8</td></tr><tr><td>混合开发</td><td>跨平台、调用原生功能、性能要求较高的场景</td><td>跨平台,调用原生功能,维护成本低</td><td>性能较原生略差,复杂功能开发受限</td><td>React Native, Weex, uni-app, taro,Flutter</td></tr></tbody></table><h2 id="我的想法"><a href="#我的想法" class="headerlink" title="我的想法"></a>我的想法</h2><h3 id="移动端"><a href="#移动端" class="headerlink" title="移动端"></a>移动端</h3><h4 id="局势分析"><a href="#局势分析" class="headerlink" title="局势分析"></a>局势分析</h4><p>移动跨端多是讨论跨android和ios两大移动系统<br>早已经给自己作掉的Windows和就没啥起色过的阿里云OS都不在讨论范围,但最近遥遥领先的鸿蒙系统确实值得重视,可以预见的是HarmonyOS将随着国力进步等各种因素,将逐渐成为真正的第三大系统,所以在国内搞移动端应用开发不得不重视。<br>因此我在技术选型的时候会综合考虑这三个系统。<br>这是我从开发者视角去看的问题,HMOS的独立意味着又有新的技术需要花费精力去学习,看似岗位会增加,但是如果作为一个企业出发,就是意味着要投入更多的人力成本去开发维护,就算不是简单的乘法1.5倍,那也绝对是徒增的资金投入,(因为HMOS的使用并不会带来用户的增加,而是将原来使用华为手机的安卓用户变成了鸿蒙用户),给本就不乐观的移动应用开发岗位狠狠来了个暴击。其实这也在某种程度上意味着原生开发在小厂是不现实的,仅仅有可能是部分中厂和大厂有财力去招揽原生应用开发人员。所有<strong>我认为原生应用的岗位在未来是会变少的,而跨端应用的岗位会增加。</strong><br>接下来我会逐个分析各个路线优劣。</p><h4 id="原生开发"><a href="#原生开发" class="headerlink" title="原生开发"></a>原生开发</h4><p>优点和缺点都是写原生带来的,原生开发的应用能优化到极致,性能最优异,能实现的功能也是最底层的;缺点就是需要为每个系统都单独开发维护,需要投入相较于其他路线更多的人力成本,且原生开发和其他俩条偏前端技术栈的道路不同,短期内是无法让前端开发者直接干原生开发的,也就是企业若要原生开发只能现招。<br>对开发者个人来说,前端开发者要进行原生开发偏离以往的技术栈</p><ul><li>安卓要在Android Studio上开发,使用Kotlin编程语言,学习OOP思想,使用Jetpack库,学会设计模式,理解android系统等等</li><li>苹果则要使用MacOS系统,在Xcode上开发,使用Swift语言,学习OOP思想,用SwiftUI,学习ios系统等等</li><li>鸿蒙我没怎么接触过,华为为鸿蒙开发也单独搞了套开发语言和IDE等开发者工具,也是要单独学习。<br>基本上走这条路和曾经的技术栈是截然不同的,我个人的感觉也是如此,每一个都是几乎全新的东西,没太多可以通过前端经验直接移用的地方。<br>我个人认为这算是一条新的技术路线了,对绝大数前端程序员来说转型的阵痛过于凶猛了。</li></ul><p>难听的已经说了,但对我个人来说若真要进行移动应用开发,原生这一套是不得不学的</p><ul><li>一是移动端的交互逻辑与Web、桌面不同:屏幕大小不一样,点击逻辑不一样。其实这一点就值得开发者去思考,毕竟能用和好用是有很大差距的。学习这一套其实也能反哺Web开发,写响应式时也能有大致思路。</li><li>二是原生铁定的有市场的,就算跨端在厉害,只要对性能有追求,无论那跨端技术吹的多牛,都是有差距的。</li><li>三是原生开发的那一套是由相应系统的母公司直接维护,能保证和系统更新同步,系统的新特性,原生开发能第一时间用上,保证有完整的API支持。</li><li>四是应用开发完打包上架走正常流程就好,没那么多幺蛾子。</li></ul><h3 id="H5开发"><a href="#H5开发" class="headerlink" title="H5开发"></a>H5开发</h3><p>我不看好,性能最低下,功能最受限,只能搞一些简单的APP。</p><h3 id="混合开发"><a href="#混合开发" class="headerlink" title="混合开发"></a>混合开发</h3><p>这就是我最近在研究的,最让我纠结的是目前国内的状况,GFW、各家手机系统魔改、巨头搞分割各占山头,这些都无疑增加了在国内搞跨端的难度。<br>先看技术栈吧,大致就是按React和Vue分,毕竟都是从前端的Web想要干移动端的事情。<br>国外的那一套React Native + expo,似乎在国内走不通。Weex和uniapp是基于Vue,我没学过Vue,被我pass了。剩下的只有taro了,还没开始动手学,该框架是京东维护着的,短期内是死不了,但绝对到不了好用级别。<br>Flutter是另外一个特色的路线,也是实现跨端,但是要使用Dart语言,渲染方法也比较特殊,本来由于我看是Google维护的挺有希望的,但最近传闻被Flutter团队被砍到50人,不知道Flutter是否能长久,我还是选择先观望个把月再决定。<br>大概的讲一下</p><table><thead><tr><th>逻辑层</th><th>渲染层</th><th>语言类型</th><th><strong>代表技术</strong></th></tr></thead><tbody><tr><td>js引擎</td><td>webview</td><td>弱类型</td><td>uniapp</td></tr><tr><td>js引擎</td><td>原生渲染</td><td>弱类型</td><td>React Native、Weex</td></tr><tr><td>dart引擎</td><td>flutter渲染引擎</td><td>强类型</td><td>Flutter</td></tr></tbody></table><p><a href="https://juejin.cn/post/7317091780826497075">App跨平台框架VS原生开发深度评测之2023版App跨平台框架历史悠久,从cordova、react native、f - 掘金</a>具体的技术不同可以看下这篇文章,看一下是什么技术就行,这篇文章纯纯uniapp的广告文,这也是我特别反感uniapp的原因,本事没多大但吹的牛x轰轰😓。</p><p>我这么分析下来,估计也能明白我的迷惑了吧,混合开发目前还是真混乱,没有一款一锤定音的技术能作为保障,都有太多的不确定性了。</p><p>综合考量下,我选择还是趟一下RN这汤浑水,之后学的差不了的时候估计会再出一篇心得体会,害。开发的话会考虑使用taro,但是就目前我不会去学,毕竟还是处于成长探索期,迫不得已采用的开发框架等我工作再受这苦吧。学完RN之后到底是深入移动开发去学原生还是转向全栈去学Node.js等到时候再抉择吧。</p><h2 id="桌面端"><a href="#桌面端" class="headerlink" title="桌面端"></a>桌面端</h2><p>我也写不明白c系语言,直接无脑Electron得了,嫌弃包大就用下Tauri。<br>Tauri也在布局移动端,但我不怎么看好,毕竟移动端和桌面端还是不一样的,不单单是代码复用的问题。</p>]]></content>
<tags>
<tag> 前端 </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第四章:Git的未来与我的感悟</title>
<link href="/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E5%9B%9B%E7%AB%A0%EF%BC%9AGit%E7%9A%84%E6%9C%AA%E6%9D%A5%E4%B8%8E%E6%88%91%E7%9A%84%E6%84%9F%E6%82%9F/"/>
<url>/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E5%9B%9B%E7%AB%A0%EF%BC%9AGit%E7%9A%84%E6%9C%AA%E6%9D%A5%E4%B8%8E%E6%88%91%E7%9A%84%E6%84%9F%E6%82%9F/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这估计就是本系列的最后一篇文章了,我将在这篇中为系列做个总结,探讨一下git的未来,分享下我洋洋洒洒写了这么多篇文章下来的个人感悟。</p><h2 id="Git的未来"><a href="#Git的未来" class="headerlink" title="Git的未来"></a>Git的未来</h2><p>Git不仅是一个强大且灵活的分布式版本控制系统,而且在过去的十几年中不断发展。其未来发展方向体现在两大方面:核心功能的改进和与生态系统中其他工具的整合。</p><h3 id="Git的持续发展"><a href="#Git的持续发展" class="headerlink" title="Git的持续发展"></a>Git的持续发展</h3><p>随着技术的发展和用户需求的变化,Git的开发者们不断对其进行优化和扩展。比如,近年来的更新中引入了以下改进:</p><ul><li><strong>性能优化</strong>:为了应对超大规模代码库的管理需求,Git引入了诸如partial clone(部分克隆)和sparse checkout(稀疏签出)等功能,这些功能让开发者能够选择性地检出代码,提高了效率。</li><li><strong>安全性提升</strong>:随着计算机算力提升,SHA-1算法安全隐患的逐渐增加,Git逐步引入了更强的哈希算法(如SHA-256),以保证数据的安全性和完整性。</li><li><strong>用户体验优化</strong>:Git的CLI体验也在逐步改进,通过更丰富的输出提示和更多的人性化选项,使得命令的执行更为直观,降低了初学者的使用门槛。</li></ul><p>未来,Git可能会继续在以下方面优化:</p><ul><li><strong>更加智能的合并和冲突解决工具</strong>:Git社区一直在探索如何改进合并机制和冲突解决工具,让复杂的分支和合并管理更加便捷、自动化。</li><li><strong>更好的UI支持</strong>:尽管Git主要使用CLI进行操作,但对于可视化需求较高的开发者,未来Git可能会在图形化界面方面投入更多,让使用变得更加直观。</li></ul><h3 id="Git与其他工具的整合"><a href="#Git与其他工具的整合" class="headerlink" title="Git与其他工具的整合"></a>Git与其他工具的整合</h3><p>Git不仅仅是一个代码管理工具,还形成了一个强大的生态系统,与多种协作和项目管理平台无缝集成,比如GitHub、GitLab、Bitbucket等。它们通过以下几种方式与Git紧密结合,极大提高了开发团队的协作效率:</p><ul><li><strong>代码审查与合并请求(Pull Request/Merge Request)</strong>:这些平台基于Git分支和变基功能,提供了直观的代码审查工具,使团队能够在代码合并前进行详细的讨论和审查,确保代码质量。</li><li><strong>持续集成与持续部署(CI/CD)</strong>:Git平台与CI/CD工具集成紧密,帮助开发者自动化构建、测试和部署过程。GitLab和GitHub等平台内置的CI/CD工具,可以自动执行测试、构建并将更改推送到生产环境中。</li><li><strong>项目管理与协作</strong>:Git平台集成了任务管理、问题跟踪、看板等功能,让开发者不仅可以在同一平台上管理代码,还可以进行项目进度的管理和跟踪,进一步提升了团队的协作效率。</li></ul><h3 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h3><p>Git已经成为现代软件开发中不可或缺的工具,不仅以其分布式、强大的版本管理体系而著称,还因其持续的改进和生态系统的构建,不断适应快速发展的开发需求。未来,Git的核心功能将会更加智能和自动化,与生态工具的集成也会更加紧密,以应对更复杂的项目和更多元的团队协作需求。通过持续的技术创新和对开发者需求的关注,Git将继续在版本控制领域中保持领先地位,为全球开发者提供更可靠的工具支持。</p><h2 id="Git对其他软件的启发"><a href="#Git对其他软件的启发" class="headerlink" title="Git对其他软件的启发"></a>Git对其他软件的启发</h2><p>具体来说,以下软件受到了Git的设计和实现的启发:</p><ol><li><p><strong>区块链技术(如Bitcoin、Ethereum)</strong><br>Git的不可变数据模型和哈希链概念启发了区块链的架构。区块链的每一个区块都有一个哈希值,类似于Git中的commit对象,每个commit包含前一个commit的哈希值来形成链式结构。这种设计保证了数据的完整性和不可篡改性,使得区块链在分布式账本中能够有效追溯并验证数据。</p></li><li><p><strong>NoSQL数据库(如CouchDB、Cassandra)</strong><br>Git的分布式存储理念启发了CouchDB和Cassandra等NoSQL数据库的设计。CouchDB采用了与Git类似的文档存储和复制机制,每个副本均包含数据的完整快照,并可在不同的节点之间合并和同步。这种模型在高并发和分布式数据环境下,减少了对集中式主服务器的依赖,提高了数据的可用性和容错能力。</p></li><li><p><strong>Docker和Kubernetes</strong><br>Docker和Kubernetes的镜像和容器管理机制从Git的克隆和分支模型中获得了启发。Docker镜像的层级式存储结构类似于Git的commit树,每一层的改变可以被独立追踪并组合成完整的容器镜像。Kubernetes在容器拉取和迁移时,采用与Git类似的“拉取-合并”策略,使得镜像可以高效分发和更新。</p></li><li><p><strong>Spark和Flink等数据处理框架</strong><br>Git的增量式数据存储和处理启发了流处理框架中对“快照”和“检查点”概念的应用。Spark和Flink利用类似的增量存储机制,可以在大规模数据处理时保存中间状态并进行快速恢复。Git对对象的增量压缩和快照机制有效降低了存储和传输成本,这一思想在流处理框架中得到了应用以提高容错性和计算性能。</p></li><li><p><strong>CI/CD平台(如Jenkins、GitLab CI)</strong><br>Git的分支和钩子机制极大地影响了CI/CD工具的设计。Jenkins和GitLab CI等平台充分利用Git的分支开发和钩子触发机制,在分支提交时自动触发代码构建、测试和部署流程。通过Git的分支模型,这些平台能够并行处理多条开发流水线,提高了团队的协作效率和软件的发布效率。</p></li><li><p><strong>文件同步工具(如rsync、Dropbox)</strong><br>Git的增量式存储和传输优化对文件同步工具如rsync、Dropbox等影响深远。rsync的差分传输机制与Git的对象存储方式类似,使用哈希对比文件变化,只传输改变的部分,以减小传输量。Dropbox也利用了增量式文件同步技术,在本地和云端之间高效同步文件,确保在多设备之间保持数据的一致性。</p></li><li><p><strong>ElasticSearch</strong><br>Git的日志和数据追踪模型启发了ElasticSearch中的文档索引管理。ElasticSearch使用类似Git的分片和副本概念,每个索引可分成多个分片,每个分片可以被复制到不同节点,实现数据的分布式存储和追溯。此外,Git的分支与合并模型也为ElasticSearch的版本管理和数据更新流程提供了灵感。</p></li><li><p><strong>Obsidian和Notion</strong><br>Obsidian和Notion等笔记管理工具的版本控制和同步机制也受到Git的影响。Obsidian支持文件版本的追踪和回溯,可以回顾文档的更改历史;Notion通过自动化的修改记录机制,保证了多人协作时数据的完整性,类似于Git对commit的管理,让用户可以在不同历史版本之间切换。</p></li><li><p><strong>ZFS文件系统</strong><br>Git对不可变数据结构的使用为ZFS等现代文件系统提供了思路。ZFS利用类似Git的快照和分支技术来存储文件数据的历史版本。每次文件系统修改后,ZFS生成一个不可变快照,允许用户访问过去的文件状态。这使得ZFS在提供数据恢复和复制功能时效率更高,也更安全。</p></li></ol><p>Git作为一个开创性的版本控制工具,不仅在软件工程领域广泛使用,也启发了数据存储、分布式系统、区块链、容器化管理等许多现代软件和系统的设计。<br>我在补充一句,我在一篇2015年的一篇文章中<a href="https://teropa.info/blog/2015/03/02/change-and-its-detection-in-javascript-frameworks.html">Change And Its Detection In JavaScript Frameworks</a>看到那时候有一种对React的Virtual DOM性能优化的方案就是利用<strong>不可变数据对象</strong>,为每个节点都计算hash值,通过比对叶节点的hash值是否改变就可以判断那一串节点是否有改变,若无改变这直接引用,就像是利用了git的<strong>快照模型</strong>,以此图求优化Virtual DOM的计算速度。</p><h2 id="我的感悟"><a href="#我的感悟" class="headerlink" title="我的感悟"></a>我的感悟</h2><p>这一系列文章对我来说确实是一个巨大的考验,我是第一次尝试写如此规模的文章,但我也是蛮有动力的写下来了,对我来说属实是受益匪浅。<br>首先,最直接的就是对Git有了更加的全面和深入的理解,在此之前我也接触了Git有近一年的时间了,在实践过程中也是一步一坎,也在期间写过两篇我的个人的学习笔记,但终归是不够全面的。在今年暑假期间就设想去写这么一篇系统的文章去仔细了解一下Git,也是一直待在我的todolist中直到我们团队轮到我做技术分享了,我才有压力与动力去着手准备写,但是也没想到规模如此庞大。<br>其次,也是证明GPT是划时代的产物了,这一系列文章中的大部分资料都是通过追问GPT得来的。有人或许会说这样直接用GPT的回答写文章的行为有什么意义,但是我认为这无非是利用好工具,如同直接和一名专家对话,可以极大的简化信息的检索过程。在没有大语言模型之前,写出一篇科普性质的文章也无非是“抄抄”互联网上的文章、“抄抄”已经出版的书籍,再结合自己的思想粘合成一篇自己的文章。倘若没有GPT,我估计我是无法完成这种程度的文章的,一定是要付出数倍于现在的所消耗的时间精力乃至金钱。<br>最后,也是启发我去思考普通大众对版本控制的需求,去思考在移动设备上类Git的实现形式。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>该系列拢共10篇,耗时三周时间,其中一周前期准备进行资料收集和大纲规划,陆续在两周中抽空写出了这一系列文章。写的时候也是动力十足,灵感横飞,多了很多不在预期规划中的知识。如今也是完美收官了,了结一个横亘在心头数月之久的念头🤪。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第三章(4):GUI工具和插件</title>
<link href="/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%884%EF%BC%89%EF%BC%9AGUI%E5%B7%A5%E5%85%B7%E5%92%8C%E6%8F%92%E4%BB%B6/"/>
<url>/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%884%EF%BC%89%EF%BC%9AGUI%E5%B7%A5%E5%85%B7%E5%92%8C%E6%8F%92%E4%BB%B6/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这篇文章是我昨天晚上躺在床上突然想起来要写的,感觉Git的<a href="https://git-scm.com/downloads/guis?os=windows">Git - GUI Clients</a>像是GitHub Desktop还是值得讲一讲的。Git官网上推荐了39款GUI客户端,我就挑几款介绍一下,主要还是带大家了解一下有这东西。此外,由于之前提到过微软收购了GitHub,也在自家IDE-VScode等中官方支持了一些可视化插件,还是挺好用的。</p><h2 id="Git-GUI工具"><a href="#Git-GUI工具" class="headerlink" title="Git GUI工具"></a>Git GUI工具</h2><p>以下是几款Git GUI工具的介绍:</p><h3 id="1-Sourcetree-Free-Git-GUI-for-Mac-and-Windows"><a href="#1-Sourcetree-Free-Git-GUI-for-Mac-and-Windows" class="headerlink" title="1. Sourcetree | Free Git GUI for Mac and Windows"></a>1. <strong><a href="https://www.sourcetreeapp.com/">Sourcetree | Free Git GUI for Mac and Windows</a></strong></h3><ul><li><strong>开发者</strong>:由Atlassian开发,免费使用。</li><li><strong>支持平台</strong>:Windows、macOS。</li><li><strong>特点</strong>:<ul><li><strong>详细的提交历史可视化</strong>:Sourcetree展示了清晰的提交历史图,帮助用户追踪项目进展。用户可以直观地查看提交、分支和合并情况。</li><li><strong>分支和合并管理</strong>:简化了分支和合并操作的复杂性,通过图形化的分支树状结构,使得Git操作更直观。</li><li><strong>子模块支持</strong>:能够管理多个子模块(或仓库),方便对大型项目的依赖进行管理。</li></ul></li><li><strong>适用场景</strong>:适合需要高效管理分支和分布式项目的用户。</li><li><strong>优缺点</strong>:<ul><li><strong>优点</strong>:界面详细、功能丰富、免费。</li><li><strong>缺点</strong>:有时会出现性能问题,特别是在管理大型仓库时。界面对新手可能略显复杂。</li></ul></li></ul><h3 id="2-GitKraken-Desktop-Free-Git-GUI-Terminal-Mac-Windows-Linux"><a href="#2-GitKraken-Desktop-Free-Git-GUI-Terminal-Mac-Windows-Linux" class="headerlink" title="2. GitKraken Desktop | Free Git GUI + Terminal | Mac, Windows, Linux"></a>2. <strong><a href="https://www.gitkraken.com/git-client">GitKraken Desktop | Free Git GUI + Terminal | Mac, Windows, Linux</a></strong></h3><ul><li><strong>开发者</strong>:由Axosoft开发,提供基础免费版,以及Pro和企业版(付费)。</li><li><strong>支持平台</strong>:Windows、macOS、Linux。</li><li><strong>特点</strong>:<ul><li><strong>现代化的用户界面</strong>:GitKraken的界面设计非常直观,操作流畅,支持Dark Mode。</li><li><strong>内置Gitflow支持</strong>:可以帮助团队采用标准的分支管理模式,确保项目的有序管理。</li><li><strong>冲突解决可视化</strong>:集成了冲突解决编辑器,通过视觉化的合并冲突工具,简化了冲突处理流程。</li><li><strong>跨平台支持</strong>:Windows、macOS、Linux平台均有支持,是跨平台开发者的理想选择。</li></ul></li><li><strong>适用场景</strong>:适合注重界面美观和交互性、需要Gitflow管理的团队或个人。</li><li><strong>优缺点</strong>:<ul><li><strong>优点</strong>:现代化的UI,功能强大,跨平台支持。</li><li><strong>缺点</strong>:高级功能需要付费订阅,资源需求较高,可能在配置较低的计算机上有性能问题。</li></ul></li></ul><h3 id="3-GitHub-Desktop-Simple-collaboration-from-your-desktop"><a href="#3-GitHub-Desktop-Simple-collaboration-from-your-desktop" class="headerlink" title="3. GitHub Desktop | Simple collaboration from your desktop"></a>3. <strong><a href="https://github.com/apps/desktop">GitHub Desktop | Simple collaboration from your desktop</a></strong></h3><ul><li><strong>开发者</strong>:由GitHub开发,完全免费。</li><li><strong>支持平台</strong>:Windows、macOS。</li><li><strong>特点</strong>:<ul><li><strong>简洁易用</strong>:设计直观简洁,适合入门用户。可以方便地进行Git克隆、提交、创建分支等基本操作。</li><li><strong>无缝GitHub集成</strong>:专为GitHub用户设计,支持GitHub上的项目管理和操作,包括创建pull request、同步项目等。</li><li><strong>简化Git流程</strong>:减少了Git操作的复杂性,是新手学习和使用Git的友好工具。</li></ul></li><li><strong>适用场景</strong>:适合GitHub用户,特别是希望快速上手并完成常规操作的新手。</li><li><strong>优缺点</strong>:<ul><li><strong>优点</strong>:完全免费,GitHub集成度高,操作简单。</li><li><strong>缺点</strong>:<strong>功能较为基础,不适合处理复杂的Git操作,也不支持非GitHub平台的项目管理。</strong></li></ul></li></ul><h3 id="4-Fork-a-fast-and-friendly-git-client-for-Mac-and-Windows"><a href="#4-Fork-a-fast-and-friendly-git-client-for-Mac-and-Windows" class="headerlink" title="4. Fork - a fast and friendly git client for Mac and Windows"></a>4. <strong><a href="https://git-fork.com/">Fork - a fast and friendly git client for Mac and Windows</a></strong></h3><ul><li><strong>开发者</strong>:Fork开发者团队,提供免费试用版,后续需要购买许可。</li><li><strong>支持平台</strong>:Windows、macOS。</li><li><strong>特点</strong>:<ul><li><strong>性能和速度</strong>:Fork专注于高效和快速的操作,即使是大型项目也能流畅运行。</li><li><strong>提交历史和合并冲突可视化</strong>:提供详细的提交历史图,支持视觉化的冲突解决编辑器,方便进行合并冲突的解决。</li><li><strong>轻量级</strong>:界面设计简洁、操作流畅。</li></ul></li><li><strong>适用场景</strong>:适合希望高性能操作Git的用户,适用于大型项目的管理。</li><li><strong>优缺点</strong>:<ul><li><strong>优点</strong>:界面轻量、操作流畅,适合大型项目。</li><li><strong>缺点</strong>:免费试用期后需要付费,且目前不支持Linux。</li></ul></li></ul><p>在选择GUI工具时,具体问题具体分析,根据自己具体的需求进行考量,比如项目复杂性、平台需求、界面偏好等。这些工具各有特色,想用就用,好用就用,不用拉到。</p><blockquote><p>我本人是不习惯使用这些GUI的,熟练掌握命令行就能完成平时的绝大多数操作了,一些不常用的功能呢GUI工具也可能开发适配的不是很好,不如直接用命令行。<br>当然不可否认的是这些工具存在的意义,我认为版本控制不仅仅是程序员需要的,面对无计算机基础的大众用户GUI的可视化、简便操作、点击交互这些特点是极好的也是极其必须的。</p></blockquote><h2 id="IDE-插件"><a href="#IDE-插件" class="headerlink" title="IDE 插件"></a>IDE 插件</h2><h3 id="常见IDE的插件"><a href="#常见IDE的插件" class="headerlink" title="常见IDE的插件"></a>常见IDE的插件</h3><h3 id="Visual-Studio-Code-GitLens"><a href="#Visual-Studio-Code-GitLens" class="headerlink" title="Visual Studio Code GitLens"></a><strong>Visual Studio Code GitLens</strong></h3><ul><li><p><strong>概述</strong>:GitLens是Visual Studio Code的强大Git扩展,提供了丰富的Git功能,帮助用户更深入地查看代码历史和变更信息。</p></li><li><p><strong>特点</strong>:</p><ul><li><strong>代码变更追溯</strong>:通过Git blame,可以查看每行代码的提交记录、作者和提交时间。</li><li><strong>增强的提交和分支管理</strong>:在VS Code中直接管理分支、创建Pull Request,并可视化查看分支图。</li><li><strong>GitHub集成</strong>:支持GitHub的Pull Request功能,便于在VS Code中进行协作开发。</li></ul></li><li><p><strong>适用人群</strong>:VS Code用户,适合希望在轻量化IDE中实现全面Git功能的开发者</p><p> <a href="https://bytestack.ai/blog/4-gui-git">Pi – A better way to git</a></p></li></ul><h3 id="Eclipse-EGit"><a href="#Eclipse-EGit" class="headerlink" title="Eclipse EGit"></a><strong>Eclipse EGit</strong></h3><ul><li><p><strong>概述</strong>:EGit是Eclipse的官方Git插件,由Eclipse基金会维护,适合习惯于Eclipse的Java开发者。</p></li><li><p><strong>特点</strong>:</p><ul><li><strong>Git基本操作</strong>:支持提交、合并、克隆、分支管理等基本操作,满足日常开发需求。</li><li><strong>JGit集成</strong>:基于Java实现的JGit库,因此无需本地安装Git即可使用,适合初学者。</li><li><strong>图形化提交历史</strong>:提供清晰的提交历史视图,便于项目管理和历史追溯。</li></ul></li><li><p><strong>适用人群</strong>:Eclipse用户,特别是Java开发者。EGit相对简单,适合基本的Git操作</p><p> <a href="https://dev.to/themeselection/best-git-gui-clients-for-developers-5023">DEV Community</a></p></li></ul><h3 id="GitHub-Extension-for-Visual-Studio"><a href="#GitHub-Extension-for-Visual-Studio" class="headerlink" title="GitHub Extension for Visual Studio"></a><strong>GitHub Extension for Visual Studio</strong></h3><ul><li><strong>概述</strong>:GitHub官方为Visual Studio用户开发的插件,提供无缝的GitHub集成,适合使用GitHub进行协作的用户。</li><li><strong>特点</strong>:<ul><li><strong>GitHub账户管理</strong>:支持直接在Visual Studio中登录GitHub账户、克隆GitHub仓库。</li><li><strong>Pull Request管理</strong>:可以查看、创建、评论和合并Pull Request,便于团队协作。</li><li><strong>代码审查</strong>:提供代码行级别的变更记录和审查功能。</li></ul></li><li><strong>适用人群</strong>:Visual Studio用户,尤其是主要在GitHub上进行项目管理和协作的开发者。</li></ul><h3 id="JetBrains家IDE插件"><a href="#JetBrains家IDE插件" class="headerlink" title="JetBrains家IDE插件"></a>JetBrains家IDE插件</h3><p>JetBrains旗下的IDE,如IntelliJ IDEA、WebStorm、PyCharm等,都内置了非常强大的Git支持,但如果需要更高级或定制的功能,以下是几款适合JetBrains IDE的Git插件:</p><h3 id="1-GitToolBox"><a href="#1-GitToolBox" class="headerlink" title="1. GitToolBox"></a>1. <strong>GitToolBox</strong></h3><ul><li><strong>功能</strong>:提供比默认Git集成更多的增强功能,包括Git blame行注释(显示最后修改每行的提交信息)、分支名称高亮、文件状态等。GitToolBox还支持自动Fetch、提交消息模板等功能,帮助提高开发效率。</li><li><strong>特点</strong>:<ul><li><strong>实时显示Git信息</strong>:实时显示每行代码的提交信息和作者,便于了解代码历史。</li><li><strong>分支高亮</strong>:分支名称在编辑器底部显示,高亮当前工作分支。</li><li><strong>多仓库支持</strong>:适合需要同时管理多个仓库的项目。</li></ul></li><li><strong>适用场景</strong>:适合需要详细代码历史和高效分支管理的开发者。</li></ul><h3 id="2-GitLive"><a href="#2-GitLive" class="headerlink" title="2. GitLive"></a>2. <strong>GitLive</strong></h3><ul><li><strong>功能</strong>:GitLive是一款增强的Git协作工具,提供实时协作功能,适合团队开发使用。它可以显示团队成员的工作进展,帮助开发者随时了解同事正在修改的文件,避免冲突。</li><li><strong>特点</strong>:<ul><li><strong>实时协作</strong>:可以看到同事当前正在编辑的文件和位置,降低代码冲突的风险。</li><li><strong>冲突提示</strong>:提前发现冲突并直接在IDE中解决。</li><li><strong>团队状态面板</strong>:实时查看团队成员的分支、修改文件、提交情况等。</li></ul></li><li><strong>适用场景</strong>:适合团队协作项目,需要实时跟踪成员动态的开发者。</li></ul><h3 id="3-Gitee"><a href="#3-Gitee" class="headerlink" title="3. Gitee"></a>3. <strong>Gitee</strong></h3><ul><li><strong>功能</strong>:Gitee提供的JetBrains插件支持直接在IDE中访问Gitee上的仓库,适合国内开发者。此插件支持项目的创建、克隆、提交、Pull Request管理等操作。</li><li><strong>特点</strong>:<ul><li><strong>Gitee账户集成</strong>:可以直接在IDE中管理Gitee上的项目。</li><li><strong>Pull Request管理</strong>:支持查看、创建和评论Pull Request,便于团队协作。</li><li><strong>简化登录</strong>:提供一键登录功能,方便管理多个Gitee账户。</li></ul></li><li><strong>适用场景</strong>:适合主要使用Gitee平台的国内开发者,特别是需要频繁提交Pull Request的团队项目。</li></ul><h3 id="4-Code-With-Me"><a href="#4-Code-With-Me" class="headerlink" title="4. Code With Me"></a>4. <strong>Code With Me</strong></h3><ul><li><strong>功能</strong>:这是JetBrains推出的远程协作插件,适合远程开发和代码审查。虽然不是专门的Git插件,但它可以结合Git的代码管理,实现多人协作和代码同步。</li><li><strong>特点</strong>:<ul><li><strong>实时共享会话</strong>:支持共享当前项目,其他用户可以实时编辑、调试。</li><li><strong>内置通话功能</strong>:支持音视频通话,便于进行远程代码审查。</li><li><strong>文件权限控制</strong>:允许主机设置其他参与者的文件访问权限。</li></ul></li><li><strong>适用场景</strong>:适合远程开发、代码审查、协同编程的场景,尤其适合需要频繁讨论和调整代码的团队。</li></ul><h3 id="5-GitHub-Pull-Requests-and-Issues"><a href="#5-GitHub-Pull-Requests-and-Issues" class="headerlink" title="5. GitHub Pull Requests and Issues"></a>5. <strong>GitHub Pull Requests and Issues</strong></h3><ul><li><strong>功能</strong>:专为GitHub用户设计的插件,允许开发者在IDE中直接管理GitHub上的Pull Request和Issue。</li><li><strong>特点</strong>:<ul><li><strong>无缝GitHub集成</strong>:可以在JetBrains IDE中直接创建、查看、评论和合并Pull Request。</li><li><strong>Issue管理</strong>:支持查看和编辑项目的GitHub Issue。</li><li><strong>代码审查和评论</strong>:便于团队合作开发,简化代码审查流程。</li></ul></li><li><strong>适用场景</strong>:适合主要在GitHub上托管代码的团队项目,有助于提高工作流效率。</li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>没啥好说的🫠</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第三章(3):子模块</title>
<link href="/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%883%EF%BC%89%EF%BC%9A%E5%AD%90%E6%A8%A1%E5%9D%97/"/>
<url>/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%883%EF%BC%89%EF%BC%9A%E5%AD%90%E6%A8%A1%E5%9D%97/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这个<strong>子模块(submodule)</strong> 也是我在写这章的时候才知道的东西😁,也是把书读厚了,学到新东西了。<br>我也没实践过但是我猜啊,比如在React Native的开发过程中,除了用RN的那一套前端代码,在需要用到某些RN无法实现的功能时,需要进行原生开发,这时候这一个项目同时又前端的代码(typescript)、Android的代码(kotlin)、ios的代码(swift),利用这个子模块(submodule)就可以在这个大项目中又对三块截然不同的代码进行细分管理。<br>嘻嘻,讲都讲到这了那我就简单提提我设想的技术路线,我之前学过一年的安卓开发,期间也接触了ios的开发,也是发现了原生开发的磨叽,想着能否实现跨端,于是发现了React Native,便开始从头来过,走前端路线三件套、React、ts、tailwind等等前端技术,最后过渡到RN,在移动端深入下去🤪🤪🤪。</p><h2 id="子模块(submodule)"><a href="#子模块(submodule)" class="headerlink" title="子模块(submodule)"></a>子模块(submodule)</h2><p>Git中的子模块(submodule)功能主要用来管理一个仓库中的多个独立的项目代码,便于将其作为另一个项目的依赖并一起管理。子模块适合那种多项目组合的场景,比如在一个大项目中嵌入另一个独立开发和管理的项目。</p><h3 id="子模块的基本概念"><a href="#子模块的基本概念" class="headerlink" title="子模块的基本概念"></a>子模块的基本概念</h3><p>子模块本质上是一个Git仓库,可以作为另一个仓库的子目录进行管理。通过子模块功能,你可以将其他项目引入你的项目中而无需将它们的代码直接复制粘贴进去。当子模块的内容更新时,你可以选择同步这些更新。</p><h3 id="子模块的基本操作命令"><a href="#子模块的基本操作命令" class="headerlink" title="子模块的基本操作命令"></a>子模块的基本操作命令</h3><ol><li><p><strong>添加子模块</strong>:</p><p> <code>git submodule add <repository-url> <path></code></p><ul><li><p>例如,将子模块添加到 <code>libs/</code> 文件夹中:</p><p> <code>git submodule add https://github.com/example/lib.git libs/lib</code></p></li><li><p>该命令会在指定路径下将外部库克隆为子模块,并在 <code>.gitmodules</code> 文件中记录子模块信息。</p></li></ul></li><li><p><strong>初始化子模块</strong>:</p><p> <code>git submodule init</code></p><ul><li>在克隆包含子模块的仓库后,使用该命令来初始化子模块配置(从 <code>.gitmodules</code> 文件中读取)。</li></ul></li><li><p><strong>更新子模块</strong>:</p><p> <code>git submodule update</code></p><ul><li><p>使用该命令来检出和更新子模块到正确的版本。</p></li><li><p>常和 <code>init</code> 一起使用,可以通过以下命令一键初始化和更新所有子模块:</p><p> <code>git submodule update --init --recursive</code></p></li></ul></li><li><p><strong>克隆包含子模块的仓库</strong>: 当克隆一个包含子模块的仓库时,可以使用 <code>--recursive</code> 选项将主仓库和所有子模块一起克隆:</p><p> <code>git clone --recursive <repository-url></code></p><ul><li>如果没有使用 <code>--recursive</code>,可以在克隆完成后通过 <code>git submodule update --init --recursive</code> 初始化和更新子模块。</li></ul></li><li><p><strong>查看子模块的状态</strong>:</p><p> <code>git submodule status</code></p><ul><li>该命令可以显示子模块的当前版本(提交 SHA-1 值)和路径。</li></ul></li><li><p><strong>更改子模块的引用版本</strong>: 在子模块目录中进入相应目录并检出所需的分支或标签,然后回到主项目提交更新:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> libs/lib</span><br><span class="line">git checkout <branch-or-tag></span><br><span class="line"><span class="built_in">cd</span> ../..</span><br><span class="line">git add libs/lib</span><br><span class="line">git commit -m <span class="string">"Update submodule to <branch-or-tag>"</span></span><br></pre></td></tr></table></figure></li><li><p><strong>删除子模块</strong>: 删除子模块比删除普通文件夹稍复杂,步骤如下:</p><ul><li><p>从 <code>.gitmodules</code> 文件中移除子模块条目。</p></li><li><p>从 <code>.git/config</code> 文件中删除子模块配置。</p></li><li><p>删除子模块的缓存信息:</p><p> <code>git rm --cached <submodule-path></code></p></li><li><p>最后,删除子模块文件夹。</p></li></ul></li></ol><h3 id="子模块的优缺点"><a href="#子模块的优缺点" class="headerlink" title="子模块的优缺点"></a>子模块的优缺点</h3><p><strong>优点</strong>:</p><ul><li>有利于模块化管理项目,特别是当不同项目之间存在依赖关系时。</li><li>每个子模块可以独立发展,不会影响到主项目。<br><strong>缺点</strong>:</li><li>操作较为复杂,特别是在多人协作或子模块频繁更新的情况下。</li><li>需要额外的维护步骤,比如每次克隆或更新项目都需要初始化和更新子模块。</li></ul><h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><ul><li><strong>忽略子模块的变动</strong>:可以在 <code>.gitmodules</code> 文件中设置子模块的跟踪策略。</li><li><strong>同步子模块更新</strong>:当主项目中的子模块引用有变更时,确保团队成员在拉取代码后执行 <code>git submodule update</code>。<br>子模块管理虽然可以方便地集成外部库,但更新、切换分支时需要特别注意保持主项目和子模块的同步。</li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>差不多了,这篇估计是本章的最后一篇🤪了,也是接近尾声了,如果之后还有什么可讲能讲的知识就还会接在本篇后面继续更新的。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第三章(2):Hooks钩子</title>
<link href="/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%882%EF%BC%89%EF%BC%9AHooks%E9%92%A9%E5%AD%90/"/>
<url>/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%882%EF%BC%89%EF%BC%9AHooks%E9%92%A9%E5%AD%90/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这篇文章我们来聚焦<code>.git/hooks/</code>这个熟悉又陌生的文件夹。我不知道读者有没有前端经验,React里也有Hook的概念,但是此hook非彼hook。不懂,我们看下去就知道了。</p><h2 id="Git-钩子的基本概念"><a href="#Git-钩子的基本概念" class="headerlink" title="Git 钩子的基本概念"></a>Git 钩子的基本概念</h2><p>以下是 Git 钩子(hooks)的详细介绍,包括使用场景、编写示例和注意事项。<br>Git的钩子(hooks)是一组钩子脚本文件,位于<code>.git/hooks</code>目录下。它们允许你在Git的特定事件(如提交、合并、推送等)发生时,自动执行自定义脚本。默认情况下,Git在<code>.git/hooks</code>目录中会提供一些样例文件,这些样例文件以<code>.sample</code>为扩展名。</p><h3 id="Git-Hooks-的分类"><a href="#Git-Hooks-的分类" class="headerlink" title="Git Hooks 的分类"></a>Git Hooks 的分类</h3><p>Git hooks分为<strong>客户端钩子</strong>和<strong>服务器端钩子</strong>两类:</p><ul><li><strong>客户端钩子</strong>:这些钩子在本地仓库运行,主要用于检查或优化提交的代码,确保符合规范,适用于代码风格检查、运行测试等常规操作。</li><li><strong>服务器端钩子</strong>:这些钩子在远程仓库服务器上运行,通常用于更高级的代码控制,比如防止不合规的代码被推送到共享仓库上。</li></ul><h4 id="客户端钩子"><a href="#客户端钩子" class="headerlink" title="客户端钩子"></a>客户端钩子</h4><p>这些钩子在本地仓库中运行,通常用于代码质量检查、测试等。</p><ol><li><p><strong>pre-commit</strong>:在提交前执行。用于检查代码是否符合要求,比如运行测试、检查代码风格等。</p></li><li><p><strong>prepare-commit-msg</strong>:在创建提交消息时触发,可以用于自动生成提交消息的内容。</p></li><li><p><strong>commit-msg</strong>:在输入提交消息后执行,通常用于检查提交消息的格式。</p></li><li><p><strong>post-commit</strong>:在提交后执行,适合执行通知、更新文档等操作。</p></li><li><p><strong>pre-rebase</strong>:在执行 rebase 之前触发,可以用于阻止不符合要求的变更。</p></li><li><p><strong>pre-push</strong>:在推送之前执行,用于检查推送的内容是否符合标准。</p></li></ol><h4 id="服务器端钩子"><a href="#服务器端钩子" class="headerlink" title="服务器端钩子"></a>服务器端钩子</h4><p>这些钩子在远程 Git 服务器上执行,通常用于处理接收到的推送。</p><ol><li><p><strong>pre-receive</strong>:在接收到推送之前执行,可以用于检查推送内容的有效性。</p></li><li><p><strong>update</strong>:与 pre-receive 类似,但可以针对每个引用(分支)做检查。</p></li><li><p><strong>post-receive</strong>:在接收到推送后执行,常用于自动部署或发送通知。</p></li></ol><h3 id="编写和使用钩子"><a href="#编写和使用钩子" class="headerlink" title="编写和使用钩子"></a>编写和使用钩子</h3><ol><li><strong>定位钩子目录</strong>:<br>每个 Git 仓库都有一个 <code>.git/hooks</code> 目录,里面包含了示例钩子(以 <code>.sample</code> 结尾的文件)。你可以选择修改这些示例或新建自己的钩子脚本。</li><li><strong>创建钩子脚本</strong>:<br>假设我们要创建一个 <code>pre-commit</code> 钩子,确保提交的代码通过 ESLint 检查。你可以在 <code>.git/hooks</code> 目录下创建一个名为 <code>pre-commit</code> 的文件,并写入以下内容:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行 ESLint 检查</span></span><br><span class="line"><span class="keyword">if</span> ! eslint .; <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"代码风格检查失败,请修复错误后再提交。"</span></span><br><span class="line"> <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure></li><li><strong>赋予可执行权限</strong>:<br>运行以下命令为脚本赋予可执行权限:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x .git/hooks/pre-commit</span><br></pre></td></tr></table></figure></li><li><strong>测试钩子</strong>:<br>进行一次提交,查看钩子是否正常工作。如果 ESLint 检查失败,提交会被阻止,终端会输出相关信息。</li></ol><h3 id="常见的使用场景示例"><a href="#常见的使用场景示例" class="headerlink" title="常见的使用场景示例"></a>常见的使用场景示例</h3><p>以下是一些常见的钩子使用场景:</p><ul><li><strong>代码风格检查</strong>:在<code>pre-commit</code>钩子中集成代码检查工具(如<code>ESLint</code>、<code>Prettier</code>),确保提交的代码符合团队约定的风格。</li><li><strong>测试自动化</strong>:在<code>pre-push</code>钩子中集成测试脚本,确保代码只有在测试通过后才能推送到远程仓库。</li><li><strong>提交信息格式化</strong>:在<code>commit-msg</code>钩子中验证提交信息是否符合规范,如检查是否包含特定的前缀(如<code>feat</code>、<code>fix</code>)。</li><li><strong>自动部署</strong>:在服务器端的<code>post-receive</code>钩子中配置自动部署脚本,将代码部署到测试或生产环境。</li></ul><h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><ul><li><strong>调试钩子</strong>:如果钩子不按预期工作,可以在脚本中添加调试信息,比如使用 <code>echo</code> 打印变量和状态信息,帮助定位问题。</li><li><strong>共享钩子</strong>:钩子通常不在 Git 版本控制中,因为它们存储在 <code>.git/hooks</code> 目录中。你可以在项目中创建一个 hooks 文件夹,将钩子脚本放入其中,然后通过文档指导团队成员手动安装。</li><li><strong>钩子的执行权限</strong>:确保钩子脚本具有执行权限,否则它不会被执行。</li><li><strong>脚本语言</strong>:钩子可以用任何可执行的脚本语言编写,常见的有 Bash、Python 等。确保在脚本开头添加合适的解释器声明(如 <code>#!/bin/bash</code> 或 <code>#!/usr/bin/env python3</code>)。</li></ul><h3 id="使用Git-Hooks的最佳实践"><a href="#使用Git-Hooks的最佳实践" class="headerlink" title="使用Git Hooks的最佳实践"></a>使用Git Hooks的最佳实践</h3><ul><li><strong>保持钩子简洁</strong>:避免在钩子中编写过于复杂的逻辑,过多的操作可能会导致Git操作变慢。</li><li><strong>使用脚本管理工具</strong>:为了管理和共享钩子,可以使用诸如<code>Husky</code>之类的工具,将钩子文件统一管理,便于团队成员同步。</li><li><strong>创建钩子模板</strong>:可以在<code>~/.git-templates/hooks</code>目录下放置常用的钩子文件,这样在初始化新的Git仓库时可以自动应用这些钩子。</li></ul><p>通过以上详细介绍,应该能够更好地理解和使用 Git 钩子,以自动化和优化你的工作流程。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>对,that’s all,就是介绍一下git的hook,我本人其实没有使用过,也是在写这系列的时候才知道有这个东东的,希望未来能用起来吧。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第三章(1):常用指令及仓库分区</title>
<link href="/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%881%EF%BC%89%EF%BC%9A%E5%B8%B8%E7%94%A8%E6%8C%87%E4%BB%A4%E5%8F%8A%E4%BB%93%E5%BA%93%E5%88%86%E5%8C%BA/"/>
<url>/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%881%EF%BC%89%EF%BC%9A%E5%B8%B8%E7%94%A8%E6%8C%87%E4%BB%A4%E5%8F%8A%E4%BB%93%E5%BA%93%E5%88%86%E5%8C%BA/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本章开始,我会笼统的介绍一下git的一些常用指令,然后挑我感兴趣的一些指令进行详细解析和拓展。这章将不像前一章那样关注概念的理解,而是逐渐将重心转移到应用上去。</p><h2 id="Git指令"><a href="#Git指令" class="headerlink" title="Git指令"></a>Git指令</h2><p>Git的指令(命令)可以分为几种主要类型,每种类型针对不同的操作和功能,其实如果你一路看下来光看名字也能猜出个所以然,前面文章中也多多少少触及到了一些Git指令。以下是主要的几种类型指令:</p><h3 id="1-配置命令"><a href="#1-配置命令" class="headerlink" title="1. 配置命令"></a>1. <strong>配置命令</strong></h3><ul><li><code>git config</code>:设置Git的全局或本地配置。</li></ul><h3 id="2-仓库命令"><a href="#2-仓库命令" class="headerlink" title="2. 仓库命令"></a>2. <strong>仓库命令</strong></h3><ul><li><code>git init</code>:初始化新的Git仓库。</li><li><code>git clone</code>:克隆远程仓库。</li></ul><h3 id="3-文件操作命令"><a href="#3-文件操作命令" class="headerlink" title="3. 文件操作命令"></a>3. <strong>文件操作命令</strong></h3><ul><li><code>git add</code>:将更改添加到暂存区。</li><li><code>git rm</code>:删除文件。</li><li><code>git mv</code>:移动或重命名文件。</li></ul><h3 id="4-提交命令"><a href="#4-提交命令" class="headerlink" title="4. 提交命令"></a>4. <strong>提交命令</strong></h3><ul><li><code>git commit</code>:提交更改。</li><li><code>git commit --amend</code>:修改上一个提交。</li><li><code>git commit -m "message"</code>:提交时直接添加提交信息。</li></ul><h3 id="5-分支和合并命令"><a href="#5-分支和合并命令" class="headerlink" title="5. 分支和合并命令"></a>5. <strong>分支和合并命令</strong></h3><ul><li><code>git branch</code>:管理分支。</li><li><code>git checkout</code>:切换分支或恢复文件。</li><li><code>git merge</code>:合并分支。</li><li><code>git rebase</code>:变基操作,将当前分支的基础更改为另一个分支。</li></ul><h3 id="6-版本历史命令"><a href="#6-版本历史命令" class="headerlink" title="6. 版本历史命令"></a>6. <strong>版本历史命令</strong></h3><ul><li><code>git log</code>:查看提交历史。</li><li>[[git diff]]:比较差异。</li><li><code>git show</code>:查看特定提交的详细信息。</li><li><code>git blame</code>:查看每一行代码的最后提交信息。</li></ul><h3 id="7-远程操作命令"><a href="#7-远程操作命令" class="headerlink" title="7. 远程操作命令"></a>7. <strong>远程操作命令</strong></h3><ul><li><code>git fetch</code>:获取远程更新。</li><li><code>git pull</code>:获取并合并远程更新。</li><li><code>git push</code>:将本地更改推送到远程。</li><li><code>git remote</code>:管理远程仓库。</li></ul><h3 id="8-标签命令"><a href="#8-标签命令" class="headerlink" title="8. 标签命令"></a>8. <strong>标签命令</strong></h3><ul><li><code>git tag</code>:创建和管理标签。</li></ul><h3 id="9-恢复和重置命令"><a href="#9-恢复和重置命令" class="headerlink" title="9. 恢复和重置命令"></a>9. <strong>恢复和重置命令</strong></h3><ul><li><code>git reset</code>:重置当前分支。</li><li><code>git checkout -- <file></code>:恢复文件到上一个提交状态。</li><li><code>git revert</code>:撤销某个提交的更改。</li><li><code>git clean</code>:清理工作目录中的未跟踪文件。</li></ul><h3 id="10-高级命令"><a href="#10-高级命令" class="headerlink" title="10. 高级命令"></a>10. <strong>高级命令</strong></h3><ul><li><code>git cherry-pick</code>:将特定提交应用到当前分支。</li><li><code>git stash</code>:暂存当前的修改,清空工作区。</li><li><code>git reflog</code>:查看引用日志,跟踪分支的历史移动。</li><li><code>git bisect</code>:二分查找提交,帮助定位引入bug的提交。</li></ul><h3 id="11-Git子模块命令-子模块命令"><a href="#11-Git子模块命令-子模块命令" class="headerlink" title="11. [[Git子模块命令|子模块命令]]"></a>11. <strong>[[Git子模块命令|子模块命令]]</strong></h3><ul><li><code>git submodule</code>:管理Git子模块,允许在一个Git仓库中包含另一个仓库。</li></ul><h3 id="12-Git-hook-钩子命令"><a href="#12-Git-hook-钩子命令" class="headerlink" title="12. [[Git hook|钩子命令]]"></a>12. <strong>[[Git hook|钩子命令]]</strong></h3><ul><li>Git支持通过钩子脚本(如<code>pre-commit</code>、<code>post-commit</code>)实现自动化操作。</li></ul><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>Git命令的类型和功能非常丰富,以上列举的命令涵盖了大部分常用的Git操作。根据不同的需求,开发者可以灵活运用这些命令来管理版本、协作开发和维护代码库。掌握这些命令将大大提高开发效率和代码管理能力。</p><h2 id="本地仓库的三个分区"><a href="#本地仓库的三个分区" class="headerlink" title="本地仓库的三个分区"></a>本地仓库的三个分区</h2><p><a href="https://blog.csdn.net/2301_80272161/article/details/138258325">Git基础-CSDN博客</a>👈这篇文章是在我初次尝试去学Git时所作,现在回头看文字稍显青涩、板板正正的,不像现在偶然会乱讲几句胡话。但其实还是干货满满的,值得一看。现在就对文中讲到的分区再重新讲解一下吧,但是具体的使用还是看我的那篇文章为好。<br>在Git的本地仓库中,有三个重要的“分区”,分别是 <strong>工作区</strong>(Working Directory)、<strong>暂存区</strong>(Staging Area,又称索引区),以及 <strong>本地仓库</strong>(Local Repository)。理解它们的关系和作用对掌握Git的操作非常关键。</p><h3 id="1-工作区(Working-Directory)"><a href="#1-工作区(Working-Directory)" class="headerlink" title="1. 工作区(Working Directory)"></a>1. 工作区(Working Directory)</h3><ul><li><strong>定义</strong>:工作区是用户直接操作文件的区域,也就是项目文件所在的目录。用户在这里编辑、删除和新增文件,修改代码等。</li><li><strong>作用</strong>:工作区中的文件状态可以分为“已修改”(Modified)和“未跟踪”(Untracked)。修改后的文件需要进入暂存区后才可以提交到本地仓库。</li><li><strong>操作示例</strong>:在工作区中编辑文件后,文件会显示为“已修改”状态。这时可以使用 <code>git status</code> 命令查看哪些文件被修改或新增。</li></ul><h3 id="2-暂存区(Staging-Area)"><a href="#2-暂存区(Staging-Area)" class="headerlink" title="2. 暂存区(Staging Area)"></a>2. 暂存区(Staging Area)</h3><ul><li><strong>定义</strong>:暂存区是一个缓存区,保存的是即将提交到本地仓库的文件快照。也可以理解为一个准备提交的文件清单,Git会在这里收集并标记所有即将进入下一个提交的更改。</li><li><strong>作用</strong>:暂存区让用户可以在一次提交中选择性地包含文件。例如,可以一次性对多个文件做出不同的修改,但只提交其中的一部分。</li><li><strong>操作示例</strong>:在工作区修改完文件后,用 <code>git add <filename></code> 命令将文件从工作区添加到暂存区。使用 <code>git status</code> 可以看到这些文件已处于暂存状态,等待提交。</li></ul><h3 id="3-本地仓库(Local-Repository)"><a href="#3-本地仓库(Local-Repository)" class="headerlink" title="3. 本地仓库(Local Repository)"></a>3. 本地仓库(Local Repository)</h3><ul><li><strong>定义</strong>:本地仓库是用户提交的所有历史记录的存储库,Git会将暂存区中的所有文件快照记录下来,以形成新的提交(commit)。</li><li><strong>作用</strong>:本地仓库包含项目的完整版本历史,并且每个提交都会生成一个唯一的哈希值标识,从而保证版本的不可变性。所有已提交的内容都保存在本地仓库中,用户可以查看历史、回滚到旧版本,甚至恢复被删除的内容。</li><li><strong>操作示例</strong>:当暂存区准备好后,使用 <code>git commit -m "message"</code> 将暂存区内容提交到本地仓库。提交后,暂存区清空,文件正式进入本地仓库的版本历史中。</li></ul><h3 id="这三个区域的工作流"><a href="#这三个区域的工作流" class="headerlink" title="这三个区域的工作流"></a>这三个区域的工作流</h3><p>一个典型的Git工作流程涉及以下步骤:</p><ol><li><strong>修改</strong>:在工作区中编辑文件,产生更改。</li><li><strong>暂存</strong>:使用 <code>git add</code> 将修改的文件放入暂存区。</li><li><strong>提交</strong>:使用 <code>git commit</code> 将暂存区中的更改提交到本地仓库。</li></ol><h3 id="图解关系"><a href="#图解关系" class="headerlink" title="图解关系"></a>图解关系</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">工作区 (Working Directory) ←→ 暂存区 (Staging Area) ←→ 本地仓库 (Local Repository)</span><br></pre></td></tr></table></figure><p>这三者之间的流动形成了Git的基本工作流,其中工作区用于修改代码,暂存区用于挑选要提交的内容,而本地仓库负责保存所有提交的历史版本。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>至于为什么要区分这三个区,我就简单的讲下,具体的可以自己找资料学,我个人认为主要是在版本回退(reset)中使用到:</p><h4 id="git-reset-的三种常用模式"><a href="#git-reset-的三种常用模式" class="headerlink" title="git reset 的三种常用模式"></a><code>git reset</code> 的三种常用模式</h4><p><code>reset</code> 有三种主要的模式,对应着不同的作用范围:</p><ol><li><p><strong><code>git reset --soft</code></strong></p><ul><li><strong>影响范围</strong>:<strong>只影响本地仓库</strong>,不影响暂存区或工作区。</li><li><strong>效果</strong>:将提交记录向后回滚,但不更改暂存区或工作区的内容。这种模式下,文件状态保持暂存,适用于更改提交信息或重新组织提交历史。</li><li><strong>用途示例</strong>:撤销最近的一次提交,合并多次提交为一次。</li></ul></li><li><p>**<code>git reset --mixed</code>**(默认模式)</p><ul><li><strong>影响范围</strong>:影响<strong>本地仓库和暂存区</strong>,但不影响工作区。</li><li><strong>效果</strong>:将提交记录回滚,同时将暂存区内容取消暂存。工作区中的文件保持不变,仍然包含上一次提交的修改。</li><li><strong>用途示例</strong>:想重新选择哪些文件暂存而不改变实际代码内容时。</li></ul></li><li><p><strong><code>git reset --hard</code></strong></p><ul><li><strong>影响范围</strong>:影响<strong>本地仓库、暂存区和工作区</strong>。</li><li><strong>效果</strong>:彻底回滚到指定提交点,将暂存区和工作区的文件全部恢复到该提交状态。任何未提交的更改都会丢失,因此需要谨慎使用。</li><li><strong>用途示例</strong>:完全恢复到某个历史提交版本,清除所有未保存的更改。</li></ul></li></ol><p>当然还有像<code>git checkout</code>、<code>git restore</code>、<code>git revert</code>、<code>git stash</code>、<code>git clean</code>这些涉及到[[与Git三个分区有关的指令]],但由于不是本章的目的,我就不具体讲解使用了,感兴趣或者要用到就自己去问GPT。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>这篇文章确实短,我也发现后面的内容也没有那么难懂了,只能说<code>轻舟已过万重山</code>,之后大概也是这样,将一些具体实践上的事情一笔带过,让读者了解一下有些什么即可。我还是坚持实用至上,当真正用到的时候自然会会的,快速推进知识迭代打开知识面。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第二章(4):分布式版本控制</title>
<link href="/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%884%EF%BC%89%EF%BC%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/"/>
<url>/tangBlog/2024/11/09/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%884%EF%BC%89%EF%BC%9A%E5%88%86%E5%B8%83%E5%BC%8F%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>也是到第二章的第四篇了,这篇我希望能结合前面讲到的快照模型、不可变数据对象、分支模型的知识,来探讨Git是如何实现<strong>分布式</strong>这件事情的,或许会捎带嘴的提一下Github之类远程托管仓库平台的兴起。</p><h2 id="Git分布式版本控制的实现"><a href="#Git分布式版本控制的实现" class="headerlink" title="Git分布式版本控制的实现"></a>Git分布式版本控制的实现</h2><p>Git的分布式版本控制系统与传统的集中式版本控制(如SVN)相比,有几个关键的不同点。Git的架构使得每个开发者的本地仓库不仅仅是一个工作副本,而是一个完整的仓库,包含了项目的所有历史记录。这种设计带来了灵活的协作模式和极大的独立性。以下是Git分布式版本控制的一些关键点:</p><h3 id="1-本地完整仓库"><a href="#1-本地完整仓库" class="headerlink" title="1. 本地完整仓库"></a>1. <strong>本地完整仓库</strong></h3><ul><li><strong>完整性</strong>:在Git中,每个开发者的本地仓库都是项目完整的历史副本,包括所有的提交记录、分支、标签等。这意味着,开发者在本地可以进行几乎所有的操作,比如查看历史、创建分支、提交代码等,而不需要连接到远程服务器。</li><li><strong>独立性</strong>:开发者不需要依赖中央服务器,甚至在没有网络连接的情况下也可以正常工作。这种离线工作的能力在许多场景下非常有用,尤其是在网络不稳定的环境中。</li><li><strong>优势</strong>:即使没有网络连接,开发者也可以进行提交、分支创建、查看历史等操作,而不依赖于中央服务器。</li></ul><h3 id="2-数据完整性和安全性"><a href="#2-数据完整性和安全性" class="headerlink" title="2. 数据完整性和安全性"></a>2. <strong>数据完整性和安全性</strong></h3><ul><li><strong>不可变的快照</strong>:每次提交在Git中都是一个快照(snapshot),它通过哈希值标识唯一存在于历史中,不可更改。这意味着任何数据的改变都会创建一个新的快照,而历史记录始终保持不变。</li><li><strong>SHA-1校验</strong>:Git使用SHA-1生成唯一的对象ID(哈希),确保每个版本快照都不可篡改。这些标识在传输和存储时可以保证数据的完整性,即便是在分布式环境中,不同开发者的对象哈希一致,也表明数据内容相同。</li><li><strong>数据安全性</strong>:哈希校验机制确保了即使分布式地存储和传输数据,代码内容仍然能保持安全、一致。</li></ul><h3 id="3-分布式数据存储"><a href="#3-分布式数据存储" class="headerlink" title="3. 分布式数据存储"></a>3. <strong>分布式数据存储</strong></h3><ul><li><strong>数据模型</strong>:Git的每个仓库都包含<strong>完整的项目快照</strong>,通过不可变的哈希标识和压缩存储对象(blob、tree、commit等),Git确保每个对象都是唯一的、完整的。</li><li><strong>数据同步</strong>:当开发者需要与他人共享代码时,可以将本地仓库推送(push)到远程仓库或从其他仓库拉取(pull)更新。这些操作不会修改历史记录,而是将新的数据添加到已有的记录中。</li></ul><h3 id="4-去中心化的协作方式(分布式协作)"><a href="#4-去中心化的协作方式(分布式协作)" class="headerlink" title="4. 去中心化的协作方式(分布式协作)"></a>4. <strong>去中心化的协作方式(分布式协作)</strong></h3><ul><li><strong>与集中式的区别</strong>:集中式版本控制系统通常依赖一个中央服务器,所有代码提交和历史记录都存储在该服务器上,开发者只能从服务器上获取最新代码并提交更改。而在Git中,团队可以使用多个远程仓库,不必有一个单一的中央服务器。</li><li><strong>多仓库协作</strong>:开发者可以自由选择要与之同步的远程仓库(比如主仓库、个人的备份仓库,甚至其他开发者的仓库)。通过<code>git remote add</code>命令,Git允许开发者添加多个远程仓库,方便进行多方同步。这种灵活性让开发者能够在不同的仓库间推送或拉取更改,适应各种团队协作模式。<br>这一特点的实践我在以前的博文中有详细的讲解过👉<a href="https://drunksweet.github.io/tangBlog/2024/06/14/Git%E6%9C%AC%E5%9C%B0%E4%B8%8E%E8%BF%9C%E7%A8%8B/">Git本地与远程 | 焦糖酒的妙妙屋</a>实际上就是创建本地分支与远程仓库建立联系后变成远程分支,然后进行push、pull等操作。</li></ul><h3 id="5-离线提交和版本管理"><a href="#5-离线提交和版本管理" class="headerlink" title="5. 离线提交和版本管理"></a>5. <strong>离线提交和版本管理</strong></h3><ul><li><strong>离线操作</strong>:由于每个本地仓库都是完整的代码库,开发者可以离线进行提交、创建分支、查看历史等操作,这样大大降低了对网络的依赖,尤其是在网络不稳定的环境下。开发者可以在本地进行频繁的提交,形成多个小版本,记录开发过程,等到合适的时候再将这些更改推送到远程仓库。</li><li><strong>提高效率</strong>:这种本地提交和历史保存的能力极大提高了开发效率,开发者在不确定何时需要同步的情况下,也可以随时保留所有的变更历史。</li><li><strong>轻松切换工作流</strong>:由于Git的分布式特性,开发者可以在本地实现完整的开发流程,而不需要频繁与中央仓库通信。<br>第一点的离线操作也就是使用本地仓库进行版本控制,Git实际上是有划分三个分区(工作区、暂存区、本地仓库)也可以看我之前写的文章<a href="https://blog.csdn.net/2301_80272161/article/details/138258325">Git基础-CSDN博客</a>头几次写文章比较文字还稚嫩🫡。</li></ul><h3 id="6-分支模型支持并行开发"><a href="#6-分支模型支持并行开发" class="headerlink" title="6. 分支模型支持并行开发"></a>6. <strong>分支模型支持并行开发</strong></h3><ul><li><strong>轻量分支</strong>:Git的分支创建和切换非常轻量,分支仅仅是一个指向特定提交的指针。这种设计让开发者能够快速创建、切换分支,进行不同功能的并行开发。</li><li><strong>非线性开发</strong>:Git支持同时进行多个分支的开发,开发者可以在本地新建分支,完成测试和开发,随后与其他人的代码整合。这种分布式结构使得分支开发变得非常高效,开发者可以在本地完成新功能的开发和测试,不影响其他开发者的进度。</li></ul><h3 id="7-强大的同步机制"><a href="#7-强大的同步机制" class="headerlink" title="7. 强大的同步机制"></a>7. <strong>强大的同步机制</strong></h3><ul><li><strong>Push和Pull操作</strong>:在分布式系统中,Git提供了灵活的同步操作,开发者可以通过<code>git push</code>将本地更改推送到远程仓库,或使用<code>git pull</code>从远程仓库拉取其他人的更改。</li><li><strong>Fetch</strong>:Git还提供了<code>git fetch</code>命令,可以获取远程仓库的更改而不立即合并,让开发者有机会在本地查看并决定如何合并代码。这样的灵活性大大减少了合并冲突的可能性,也增加了协作的稳定性。</li><li><strong>Merge</strong>:<code>git merge</code>合并操作能够将分支的并行开发成果整合到一起,自动检测和合并更改,并在冲突出现时提供解决方法。</li></ul><h3 id="8-补丁分享与变基(Rebase)"><a href="#8-补丁分享与变基(Rebase)" class="headerlink" title="8. 补丁分享与变基(Rebase)"></a>8. <strong>补丁分享与变基(Rebase)</strong></h3><ul><li><strong>补丁分享</strong>:Git支持将代码的更改生成补丁文件,通过<code>git format-patch</code>生成的补丁文件可以通过各种方式分享给其他开发者。开发者可以应用补丁进行合并,实现分布式协作。</li><li><strong>变基(Rebase)</strong>:Git的<code>rebase</code>功能允许开发者将分支的基准更改到其他分支之上,清理历史记录,使之看起来像是线性的。变基在多个开发者并行开发时非常有用,可以让提交历史更加简洁、易读。</li></ul><h3 id="9-多方协作与贡献"><a href="#9-多方协作与贡献" class="headerlink" title="9. 多方协作与贡献"></a>9. <strong>多方协作与贡献</strong></h3><ul><li><strong>去中心化的协作模型</strong>:Git的设计不要求有一个“主仓库”或“中央服务器”,开发者可以通过多仓库之间的相互拉取和推送进行协作。开源项目中尤其会采用这种方式,多个开发者可以通过拉取彼此的代码、推送到公共仓库等方式来进行协作。</li><li><strong>代码审查与拉取请求</strong>:在没有GitHub的情况下,开发者可以通过Git的分支和变基操作,手动完成代码审查流程。代码可以通过补丁文件或其他分支共享方式进行讨论和审查。</li></ul><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>Git的分布式版本控制赋予开发者本地完整的仓库,使他们在离线环境中也能高效工作,降低了对中央服务器的依赖。同时,Git的轻量分支、变基、补丁共享等特性帮助开发者高效处理并行开发和代码整合。SHA-1哈希值和不可变快照的设计保证了数据的安全性和一致性。这种去中心化的模式不仅适合团队内部的协作开发,也非常适合开源项目的贡献和分布式的代码管理。</p><h2 id="远程协作"><a href="#远程协作" class="headerlink" title="远程协作"></a>远程协作</h2><p>GitHub 是一个基于 Git 的代码托管和协作平台,允许开发者将代码存储在云端并与他人合作开发。在具体介绍GitHub之前,让我们先看看在它诞生之前,Git是通过什么样的方式实现远程协助的👇<br>Git主要通过“裸仓库”(bare repository)和分布式的<strong>服务器存储</strong>方式来实现远程协作。虽然那时候没有像GitHub这样的集中式平台,但团队仍然可以通过手动设置和使用远程仓库实现分布式协作。以下是Git在GitHub出现前常用的协作方式:</p><h3 id="1-裸仓库(Bare-Repository)"><a href="#1-裸仓库(Bare-Repository)" class="headerlink" title="1. 裸仓库(Bare Repository)"></a>1. <strong>裸仓库(Bare Repository)</strong></h3><ul><li><strong>概念</strong>:裸仓库是一个没有工作目录的Git仓库,用于共享和协作。它仅包含版本数据(即<code>.git</code>目录中的内容),没有实际的项目文件。这种仓库的设置方式可以防止直接在其中进行开发操作,避免了冲突的发生。</li><li><strong>用途</strong>:开发团队可以在共享的服务器上创建一个裸仓库,让每个开发者将它作为远程仓库进行操作(如<code>git push</code>和<code>git pull</code>),从而在裸仓库上共享代码。</li><li><strong>设置过程</strong>:裸仓库可以通过以下命令创建:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git init --bare</span><br></pre></td></tr></table></figure>然后开发者将这个仓库作为远程仓库添加到自己的本地仓库,通过它来与其他开发者共享代码。</li><li><strong>协作方式</strong>:开发者各自在本地开发,通过裸仓库定期推送自己的更改,并拉取其他人推送的更改。这种协作方式虽然没有GitHub的UI方便,但同样可以完成代码同步和协作。</li></ul><h3 id="2-通过SSH协议进行仓库访问"><a href="#2-通过SSH协议进行仓库访问" class="headerlink" title="2. 通过SSH协议进行仓库访问"></a>2. <strong>通过SSH协议进行仓库访问</strong></h3><ul><li><strong>SSH远程访问</strong>:在没有GitHub的情况下,开发者通常通过SSH连接服务器来访问和操作远程仓库。团队会在一台公共服务器(如团队内部的Linux服务器)上创建一个裸仓库,并为每个成员设置SSH访问权限。</li><li><strong>协作流程</strong>:开发者通过设置远程仓库地址,将裸仓库的URL作为<code>origin</code>或其他远程名。SSH允许安全传输数据,而Git通过SSH实现了分布式代码管理。</li><li><strong>示例</strong>:例如,某个团队可以在服务器的<code>/opt/repo/project.git</code>目录下创建裸仓库,开发者通过以下命令添加并推送代码:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git remote add origin user@server:/opt/repo/project.git</span><br><span class="line">git push origin main</span><br></pre></td></tr></table></figure></li></ul><h3 id="3-通过Email补丁交换(Email-Patch-Exchange)"><a href="#3-通过Email补丁交换(Email-Patch-Exchange)" class="headerlink" title="3. 通过Email补丁交换(Email Patch Exchange)"></a>3. <strong>通过Email补丁交换(Email Patch Exchange)</strong></h3><ul><li><strong>发送补丁</strong>:Git支持通过电子邮件交换补丁。开发者可以使用<code>git format-patch</code>命令生成补丁文件,并通过电子邮件发送给其他成员。接收方可以用<code>git am</code>命令应用补丁,将更改合并到自己的分支中。</li><li><strong>使用方式</strong>:这种方法在早期的开源项目中非常常见,特别适用于分布式团队。Linus Torvalds在维护Linux内核时也常用此方法来收集开发者的贡献。</li><li><strong>示例</strong>:创建补丁并发送的步骤如下:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git format-patch -1 <commit></span><br><span class="line"><span class="comment"># 然后开发者可以通过电子邮件发送补丁文件</span></span><br></pre></td></tr></table></figure></li></ul><h3 id="4-挂载网络文件系统(NFS、SMB)"><a href="#4-挂载网络文件系统(NFS、SMB)" class="headerlink" title="4. 挂载网络文件系统(NFS、SMB)"></a>4. <strong>挂载网络文件系统(NFS、SMB)</strong></h3><ul><li><strong>共享文件系统</strong>:在一些小型团队中,可以通过网络文件系统(如NFS或SMB)来共享仓库。团队将裸仓库放在网络共享文件夹中,开发者可以直接访问该目录并进行操作。</li><li><strong>限制</strong>:这种方式适合小型团队,且对网络速度有较高要求,无法实现真正的分布式协作。尽管如此,这种方法仍然提供了一种灵活的代码共享方式。</li></ul><h3 id="5-分布式多仓库模型"><a href="#5-分布式多仓库模型" class="headerlink" title="5. 分布式多仓库模型"></a>5. <strong>分布式多仓库模型</strong></h3><ul><li><strong>点对点协作</strong>:在没有集中式平台时,开发者可以在各自的机器上建立各自的仓库,通过互相添加对方的仓库作为远程仓库进行协作。这种方式虽然略显复杂,但适合一些特定的分布式开发团队。</li><li><strong>例子</strong>:开发者A和B可以直接将对方的仓库添加为远程仓库,通过<code>git fetch</code>和<code>git push</code>来直接共享代码,而无需依赖集中式的服务器。<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote add developerB ssh://developerB@hostname:/path/to/repo.git</span><br></pre></td></tr></table></figure></li></ul><h3 id="6-协作工作流"><a href="#6-协作工作流" class="headerlink" title="6. 协作工作流"></a>6. <strong>协作工作流</strong></h3><ul><li>在没有GitHub的时代,Git的协作工作流更依赖于良好的沟通和手动操作。团队可以制定清晰的分支管理策略(例如将一个稳定分支作为主分支),并约定合并和提交的规范,以避免冲突和版本混乱。</li></ul><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>在GitHub诞生之前,Git通过裸仓库、SSH访问、电子邮件补丁、共享文件系统等方式,仍然能够支持分布式开发。虽然这些方法在用户体验和便捷性上不如集中式平台,但它们能够有效地实现代码分发、同步和协作,使得分布式版本控制在小型团队和开源项目中同样有效。</p><h3 id="GitHub简史"><a href="#GitHub简史" class="headerlink" title="GitHub简史"></a>GitHub简史</h3><p>然后在让我们初略的了解一下github的故事。还是得讲回当初我在B站看到的北游老土的视频<a href="https://www.bilibili.com/video/BV1i54y1Q7r2/?spm_id_from=333.788.recommend_more_video.0">GitHub的崛起_哔哩哔哩_bilibili</a>。<br>首先必须需要澄清的一点就是GitHub不是Git的缔造者Linus创造的(我看过有好几篇文章弄错了),GitHub成立于2008年,是由Tom Preston-Werner、Chris Wanstrath、P. J. Hyett和Scott Chacon共同创立的,他们希望通过创建一个更便捷的代码托管和协作平台,来改善开发者的工作流程。以下是GitHub的发展历程和一些关键事件:</p><h3 id="1-2008-创立"><a href="#1-2008-创立" class="headerlink" title="1. 2008 - 创立"></a>1. <strong>2008 - 创立</strong></h3><ul><li>GitHub在2008年正式推出。它基于由Linus Torvalds开发的分布式版本控制系统Git,提供一个图形界面来管理代码库,并且引入了社交元素(如关注、Star和Fork)以鼓励社区的互动和协作。</li></ul><h3 id="2-2009-快速增长与开源"><a href="#2-2009-快速增长与开源" class="headerlink" title="2. 2009 - 快速增长与开源"></a>2. <strong>2009 - 快速增长与开源</strong></h3><ul><li>GitHub因其简便的协作方式而迅速受到开发者欢迎,特别是在开源社区中。开源项目和企业逐渐开始将代码托管在GitHub上,项目的“开源”成为一大亮点。2009年,GitHub用户数突破10万,平台上的开源项目数也迅速增长。</li></ul><h3 id="3-2011-成为开发者首选平台"><a href="#3-2011-成为开发者首选平台" class="headerlink" title="3. 2011 - 成为开发者首选平台"></a>3. <strong>2011 - 成为开发者首选平台</strong></h3><ul><li>GitHub已经成为开发者最受欢迎的代码托管平台之一,吸引了大量个人开发者、开源项目和企业用户。2011年,GitHub的用户数突破百万,许多知名的开源项目,如Ruby on Rails、Node.js等,都在GitHub上托管。</li></ul><h3 id="4-2012-推出GitHub-Enterprise"><a href="#4-2012-推出GitHub-Enterprise" class="headerlink" title="4. 2012 - 推出GitHub Enterprise"></a>4. <strong>2012 - 推出GitHub Enterprise</strong></h3><ul><li>GitHub为企业用户推出了GitHub Enterprise,这是一种适用于企业内部部署的版本,让公司能够在自己的基础设施上运行GitHub。GitHub Enterprise帮助企业在保障安全的前提下使用GitHub的功能,吸引了大量商业客户。</li></ul><h3 id="5-2014-开始全球扩展"><a href="#5-2014-开始全球扩展" class="headerlink" title="5. 2014 - 开始全球扩展"></a>5. <strong>2014 - 开始全球扩展</strong></h3><ul><li>GitHub在全球范围内扩展,成为世界上最大的代码托管平台之一。此时,GitHub已经吸引了数百万开发者用户,并且逐步增加了如<strong>代码审查</strong>、<strong>项目管理</strong>和<strong>安全扫描</strong>等功能,以便支持更大规模和更复杂的开发团队的需求。</li></ul><h3 id="6-2015-2017-开发者友好功能的增多"><a href="#6-2015-2017-开发者友好功能的增多" class="headerlink" title="6. 2015 - 2017 - 开发者友好功能的增多"></a>6. <strong>2015 - 2017 - 开发者友好功能的增多</strong></h3><ul><li>GitHub逐步增强了平台功能,引入了Pull Requests评论、代码评论、组织管理等新特性,并在GitHub Pages和项目文档管理方面不断完善,进一步吸引了开源项目和企业用户。</li></ul><h3 id="7-2018-微软收购"><a href="#7-2018-微软收购" class="headerlink" title="7. 2018 - 微软收购"></a>7. <strong>2018 - 微软收购</strong></h3><ul><li>2018年6月,微软宣布以75亿美元收购GitHub。这次收购在开发者社区引起了广泛讨论,部分开发者担心GitHub会失去独立性。微软承诺保持GitHub的开放性和独立性,同时对其进行更多投资。之后,GitHub的发展更加多元化,并与微软旗下的Visual Studio等开发工具深度集成。</li></ul><h3 id="8-2019-GitHub-Actions的推出"><a href="#8-2019-GitHub-Actions的推出" class="headerlink" title="8. 2019 - GitHub Actions的推出"></a>8. <strong>2019 - GitHub Actions的推出</strong></h3><ul><li>GitHub推出了GitHub Actions,一种CI/CD(持续集成/持续交付)工具,帮助开发者自动化工作流,简化构建、测试和部署过程。GitHub Actions的推出使GitHub在开发流程自动化方面更具竞争力。</li></ul><h3 id="9-2020-免费私有仓库与新功能"><a href="#9-2020-免费私有仓库与新功能" class="headerlink" title="9. 2020 - 免费私有仓库与新功能"></a>9. <strong>2020 - 免费私有仓库与新功能</strong></h3><ul><li>GitHub宣布为所有用户提供免费的私有仓库,并且支持更多的协作功能(如Unlimited Collaborators)。此外,GitHub还推出了GitHub Codespaces,使开发者能够直接在浏览器中编写和调试代码。2020年GitHub还发布了“Copilot”,一个基于人工智能的编程助手,帮助开发者更快地编写代码。</li></ul><h3 id="10-2021-开源安全性的新方向"><a href="#10-2021-开源安全性的新方向" class="headerlink" title="10. 2021 - 开源安全性的新方向"></a>10. <strong>2021 - 开源安全性的新方向</strong></h3><ul><li>GitHub对代码安全性和开源供应链安全性提出更高要求,加入了自动漏洞扫描、代码依赖分析等功能。同时,GitHub Sponsors进一步支持开源开发者的资助计划,以帮助开源项目获得经济支持。</li></ul><h3 id="11-目前的发展"><a href="#11-目前的发展" class="headerlink" title="11. 目前的发展"></a>11. <strong>目前的发展</strong></h3><ul><li>GitHub不断扩展功能,包括GitHub Packages(用于发布和管理包)、GitHub Discussions(用于项目交流)、更强大的API支持等。GitHub依旧是全球最大的代码托管和协作平台之一,支持数百万开发者、企业和开源项目的需求。</li></ul><h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><p>GitHub通过不断创新和优化,不仅成为了开发者社区的核心平台,还推动了全球开源社区的发展,逐渐成为集代码托管、项目管理、安全保障和社区支持于一体的完整开发生态系统。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>呼,该篇是第二章的最后一篇文章了,本章系统详细的介绍了Git核心理念的实现机制,真是环环相扣,不得不佩服Linus等开发人员的智慧。同时也简单的拓展了一些关于区块链、GitHub的知识,主要我目前的见识也就这点🫠。<br>下一章节我会介绍一些前面文章中没讲到的漏网之鱼,既然决定开了这一系列那就尽量做到有始有终,追求知识的全面性。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第二章(3):分支模型</title>
<link href="/tangBlog/2024/11/07/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%883%EF%BC%89%EF%BC%9A%E5%88%86%E6%94%AF%E6%A8%A1%E5%9E%8B/"/>
<url>/tangBlog/2024/11/07/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%883%EF%BC%89%EF%BC%9A%E5%88%86%E6%94%AF%E6%A8%A1%E5%9E%8B/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a><strong>前言</strong></h2><p>呜呜呜🫡🫡🫡也是到第三篇了,我打算在该篇主要讲git的<strong>本地分支</strong>的相关内容,而<strong>远程分支</strong>则留给下一篇<strong>分布式版本控制</strong>。由于<code>.git/ref/</code>文件下还有tag,那就同时补充介绍一下之前一直忽视的<strong>tag</strong>吧,我个人感觉是没啥用。<br>其实这一块内容我6月份的一篇文章中已经讲解了具体的命令操作([[Git本地与远程]]),本篇则更加关注于概念和原理。</p><h2 id="分支模型"><a href="#分支模型" class="headerlink" title="分支模型"></a>分支模型</h2><p>Git 的分支模型是一种灵活且高效的版本控制机制,<strong>通过指针和哈希引用管理分支</strong>,能够轻松支持并行开发、功能隔离和协作。这种实现方式既节省空间,又使得分支操作非常迅速。以下是 Git 分支模型的详细讲解:</p><h3 id="1-分支的基本概念"><a href="#1-分支的基本概念" class="headerlink" title="1. 分支的基本概念"></a>1. 分支的基本概念</h3><ul><li><strong>分支</strong>是 Git 的核心特性,它允许你从主线代码中分离出独立的开发路径。分支的创建、切换和合并速度很快,使得开发人员可以并行进行新功能的开发、修复 bug 或实验,而不会影响主分支。</li><li>默认情况下,Git 会有一个主分支,通常称为 <code>main</code> 或 <code>master</code>(弗洛伊德之死黑命贵运动席卷全球后由于master有主人的意义,为避免种族歧视嫌疑,git现在默认的主分支名改为了main,github也是如此,当然分支的默认名称可以通过config设置成任何)。</li></ul><h3 id="2-基于指针的分支实现"><a href="#2-基于指针的分支实现" class="headerlink" title="2. 基于指针的分支实现"></a>2. 基于指针的分支实现</h3><h4 id="利用不可变数据对象"><a href="#利用不可变数据对象" class="headerlink" title="利用不可变数据对象"></a>利用不可变数据对象</h4><p>之前的文章已经介绍过了Git 的对象存储分为四种类型:</p><ul><li><strong>Blob(数据对象)</strong>:存储文件内容的对象。</li><li><strong>Tree(树对象)</strong>:表示一个目录结构,包含了该目录下所有文件和子目录的 Blob 和 Tree 的引用。</li><li><strong>Commit(提交对象)</strong>:保存一次提交的快照,包括提交者信息、时间戳、提交信息,以及指向父提交和 Tree 对象的指针。</li><li><strong>Tag(标签对象)</strong>:用于对某个提交创建标记(在后面文章会提到,和commit很像,辅助标记重要的commit用的)。<br>每个对象都用其 <strong>SHA-1 哈希值</strong>标识和引用,这种不可变的哈希标识是 Git 能够实现高效分支管理的基础。</li></ul><h4 id="分支的本质是指针"><a href="#分支的本质是指针" class="headerlink" title="分支的本质是指针"></a>分支的本质是指针</h4><p>而在 Git 中<strong>分支的本质</strong>,一个分支其实是一个指向某个提交对象的<strong>指针(引用)</strong></p><ul><li><strong>引用(Refs)</strong>:Git 将分支存储在 <code>.git/refs/heads/</code> 目录中,每个分支名称对应一个文件,文件内容是一个 40 字符长的 SHA-1 哈希值,这个值指向该分支的最后一次提交。(远程分支则存储在<code>.git/refs/remotes/</code>)</li><li><strong>HEAD 指针</strong>:HEAD 是一个特殊的指针,它指向当前被检出的分支。通过切换分支,Git 会更新 HEAD 的指向,使其指向新的分支文件。</li><li>Git 的分支本质上是一个指向提交对象的<strong>可移动指针</strong>。每个分支指针都指向该分支的最新提交对象(commit)。</li><li>Git 使用一个特殊的 <code>HEAD</code> 指针来表示当前活动分支,<code>HEAD</code> 始终指向当前分支的最新提交对象。例如:<figure class="highlight plaintext"><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">main --> C3</span><br><span class="line">HEAD --> main</span><br></pre></td></tr></table></figure>上面的结构表示 <code>main</code> 分支指向提交 <code>C3</code>,而 <code>HEAD</code> 指向 <code>main</code>,代表我们当前位于 <code>main</code> 分支上。</li></ul><h4 id="分支的创建与切换"><a href="#分支的创建与切换" class="headerlink" title="分支的创建与切换"></a>分支的创建与切换</h4><p><strong>创建分支</strong>:当你使用 <code>git branch <branch-name></code> 创建一个新分支时,在 Git 中创建分支时,实际上只是创建了一个新指针,并不会复制文件或创建新的目录。相反,它会:</p><ul><li>在 <code>.git/refs/heads/</code> 目录下创建一个新的分支文件。</li><li>将当前分支的最后一个提交的哈希值写入新分支文件中。<br>由于分支文件只是一个包含 SHA-1 哈希的文本文件,因此创建分支的速度非常快、占用存储空间极少。<br>比如,从 <code>main</code> 分支的 <code>C3</code> 提交创建一个 <code>feature</code> 分支,Git 只会创建一个名为 <code>feature</code> 的指针,也指向 <code>C3</code>:<figure class="highlight plaintext"><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">main --> C3</span><br><span class="line">feature --> C3</span><br></pre></td></tr></table></figure><strong>切换分支</strong>:切换分支时,Git 会将 <code>HEAD</code> 指针指向新分支,而 <code>HEAD</code> 所指向的内容即为工作目录中的代码版本。<br>例如,从 <code>main</code> 切换到 <code>feature</code> 分支后:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">main --> C3</span><br><span class="line">feature --> C3</span><br><span class="line">HEAD --> feature</span><br></pre></td></tr></table></figure>切换到 <code>feature</code> 后,<code>HEAD</code> 指向 <code>feature</code>,接下来的所有操作都将记录在 <code>feature</code> 分支上,不影响 <code>main</code>。</li></ul><h4 id="提交与分支指针的前移"><a href="#提交与分支指针的前移" class="headerlink" title="提交与分支指针的前移"></a>提交与分支指针的前移</h4><ul><li><strong>在分支上提交更改</strong>:当你在分支上提交新的更改时,Git 会生成一个新的提交对象,并将该分支的指针更新到新提交的哈希值。其他分支的指针不会受到影响。</li><li><strong>分支的移动</strong>:分支实际上是一个移动的指针,每次提交都会使当前分支指针从原来的提交指向新的提交。<br>假设在 <code>feature</code> 分支上进行了一个新提交 <code>C4</code>,Git 会让 <code>feature</code> 指针指向 <code>C4</code>,而 <code>main</code> 仍然停留在 <code>C3</code>,从而保持分支的独立性:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">main --> C3</span><br><span class="line">feature --> C4</span><br><span class="line">HEAD --> feature</span><br></pre></td></tr></table></figure></li></ul><h4 id="合并分支"><a href="#合并分支" class="headerlink" title="合并分支"></a>合并分支</h4><p>开发完成后,通常需要将新分支合并回主分支,Git 会生成一个新的合并提交对象,包含两个父提交对象,确保变更历史完整。合并分支(<code>git merge</code>)将不同分支的更改整合到一起:</p><ul><li><strong>快进合并</strong>:当目标分支的 HEAD 可以直接指向源分支的最新提交时,Git 会直接移动指针,称为快进合并(Fast-Forward)。</li><li><strong>三方合并</strong>:如果分支出现分叉,Git 会基于两条分支的最新提交及其最近的共同祖先生成一个新的合并提交,称为三方合并(Three-Way Merge)。<br>比如,将 <code>feature</code> 分支合并到 <code>main</code> 后,Git 会生成合并提交 <code>C5</code>,连接了 <code>C3</code> 和 <code>C4</code>:<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">main --> C5</span><br><span class="line"> / \</span><br><span class="line"> C3 C4</span><br><span class="line">feature --> C4</span><br></pre></td></tr></table></figure>此时 <code>main</code> 分支指向 <code>C5</code>,而 <code>feature</code> 分支保持在 <code>C4</code>。</li></ul><h4 id="分支的删除"><a href="#分支的删除" class="headerlink" title="分支的删除"></a>分支的删除</h4><p>删除分支时,Git 只会删除该分支的指针,不会删除任何提交记录,因为所有提交仍然可以通过其 SHA-1 哈希访问到。只有在提交对象没有任何引用指向时,才会通过 Git 的垃圾回收机制(<code>git gc</code>)最终被清理。<br>例如删除 <code>feature</code> 分支只是删除指针 <code>feature</code>,不影响提交 <code>C4</code> 和其他内容。</p><h4 id="分支切换的过程"><a href="#分支切换的过程" class="headerlink" title="分支切换的过程"></a>分支切换的过程</h4><ul><li><strong>更新 HEAD</strong>:切换分支时,Git 会更新 HEAD,使其指向新的分支。</li><li><strong>更新工作区和暂存区</strong>:Git 会将工作区和暂存区内容更新为新分支指向的提交对象中的内容。</li></ul><h3 id="3-Git-分支的轻量级实现优势"><a href="#3-Git-分支的轻量级实现优势" class="headerlink" title="3. Git 分支的轻量级实现优势"></a>3. Git 分支的轻量级实现优势</h3><ul><li><strong>高效</strong>:分支操作速度快,仅为指针操作,因此创建、切换、删除等操作不会耗费太多资源。</li><li><strong>节省空间</strong>:Git 不会为分支创建完整的代码快照,而是通过哈希引用追踪更改的部分,这极大节省了存储空间。</li><li><strong>历史记录完整</strong>:每次提交都会生成一个唯一的 SHA-1 哈希值,确保了提交内容的完整性和安全性。</li></ul><h2 id="分支指令"><a href="#分支指令" class="headerlink" title="分支指令"></a>分支指令</h2><h3 id="1-分支的创建和管理"><a href="#1-分支的创建和管理" class="headerlink" title="1. 分支的创建和管理"></a>1. 分支的创建和管理</h3><ul><li>使用命令 <code>git branch <branch-name></code> 创建新分支。</li><li>切换到指定分支使用 <code>git checkout <branch-name></code> 或者结合创建和切换的 <code>git checkout -b <branch-name></code>。</li><li>查看所有分支可以使用 <code>git branch</code> 命令。</li></ul><h3 id="2-合并分支"><a href="#2-合并分支" class="headerlink" title="2. 合并分支"></a>2. 合并分支</h3><ul><li>开发完成后,可以将分支的更改合并回主分支。使用 <code>git merge <branch-name></code> 进行合并。</li><li>合并时可能会产生冲突,需要手动解决冲突后再提交合并结果。</li></ul><h3 id="3-分支策略"><a href="#3-分支策略" class="headerlink" title="3. 分支策略"></a>3. 分支策略</h3><ul><li><strong>Git Flow</strong>:一种常用的分支策略,适用于需要稳定版本的项目。包括:<ul><li><code>master</code>:生产环境代码。</li><li><code>develop</code>:开发分支,用于合并所有特性分支。</li><li><code>feature</code>:用于新功能开发的分支。</li><li><code>release</code>:准备发布的分支。</li><li><code>hotfix</code>:快速修复生产环境问题的分支。</li></ul></li><li><strong>GitHub Flow</strong>:更简单的策略,通常用于持续交付的项目。包括:<ul><li>从 <code>main</code> 创建特性分支进行开发。</li><li>提交完成后通过 Pull Request 将更改合并回 <code>main</code>。</li></ul></li></ul><h3 id="4-分支的删除"><a href="#4-分支的删除" class="headerlink" title="4. 分支的删除"></a>4. 分支的删除</h3><ul><li>使用 <code>git branch -d <branch-name></code> 删除本地分支。</li><li>使用 <code>git push origin --delete <branch-name></code> 删除远程分支。</li></ul><h3 id="5-常见的命令"><a href="#5-常见的命令" class="headerlink" title="5. 常见的命令"></a>5. 常见的命令</h3><ul><li>创建新分支:<code>git branch <branch-name></code></li><li>切换分支:<code>git checkout <branch-name></code></li><li>合并分支:<code>git merge <branch-name></code></li><li>删除分支:<code>git branch -d <branch-name></code></li><li>查看分支:<code>git branch</code></li><li>查看所有远程分支:<code>git branch -r</code></li></ul><h2 id="Tag"><a href="#Tag" class="headerlink" title="Tag"></a>Tag</h2><p>在Git中,<strong>tag</strong>(标签)用于给特定的提交(commit)打上标记,通常用于标识重要的版本或里程碑。标签和提交类似于“快照”,一旦创建就不会改变,这使它非常适合在代码的历史记录中标记版本,如发布版本v1.0.0、v2.1.3等。<br>标签的引用会被记录在<code>.git/refs/tags/</code>目录中。每个标签的引用会对应一个文件,文件内容就是该标签所指向的提交哈希值。<br>Git中的标签主要分为两种:</p><ol><li><strong>轻量级标签(Lightweight Tag)</strong>:这是一个简单的指向特定提交的引用,本质上类似一个不会移动的分支,不包含额外的元数据。</li><li><strong>注释标签(Annotated Tag)</strong>:这是更正式的标签类型。它包含标签名、日期、作者、签名(可选)、注释等信息,适合用在需要明确说明版本的场景中。<br>在Git中,<strong>标签(tag)会生成一个唯一的哈希值</strong>。这个哈希值由Git生成,用于唯一标识该标签,就像提交(commit)对象一样。<br>标签的生成机制和Git内部对象存储的方式有关。Git中所有的数据(包括标签、提交、树、文件)都是对象,每个对象都有一个唯一的哈希值,使用了SHA-1算法生成。标签的类型决定了它如何被存储和引用:</li></ol><h3 id="Git标签的实现机制"><a href="#Git标签的实现机制" class="headerlink" title="Git标签的实现机制"></a>Git标签的实现机制</h3><p>Git标签本质上是一个Git对象,可以在仓库中找到并引用。标签对象的生成和类型不同,生成的哈希值也稍有不同。</p><h4 id="两种不同的标签"><a href="#两种不同的标签" class="headerlink" title="两种不同的标签"></a>两种不同的标签</h4><h5 id="1-轻量级标签(Lightweight-Tag)"><a href="#1-轻量级标签(Lightweight-Tag)" class="headerlink" title="1. 轻量级标签(Lightweight Tag)"></a>1. <strong>轻量级标签(Lightweight Tag)</strong></h5><p>轻量级标签仅仅是一个<strong>指向提交对象的“符号引用”</strong>,它<strong>没有独立的标签对象</strong>。它更像是给某个提交“起了个别名”。轻量级标签不会额外存储元数据(如作者、日期、注释等),只是简单地将标签名称指向指定的提交。<br>其存储方式为简单的指针,<strong>不生成独立的标签对象</strong>,因此不会创建额外的Git对象或哈希值,仅通过标签名称引用对应的提交哈希值。</p><h5 id="2-注释标签(Annotated-Tag)"><a href="#2-注释标签(Annotated-Tag)" class="headerlink" title="2. 注释标签(Annotated Tag)"></a>2. <strong>注释标签(Annotated Tag)</strong></h5><p>注释标签是更正式的标签类型,Git会为其生成一个单独的“标签对象”(tag object)。创建注释标签时,Git生成了一个新对象,用于保存以下信息:</p><ul><li>标签名称</li><li>标签的作者(tagger)</li><li>标签的创建时间</li><li>标签的注释内容</li><li>标签所指向的提交对象<br>注释标签的生成过程如下:</li></ul><ol><li><strong>创建标签对象</strong>:Git使用SHA-1算法生成标签的哈希值,并将标签的元信息和所指向的提交信息存储在标签对象中。</li><li><strong>指向提交对象</strong>:标签对象包含指向特定提交的指针,因此这个标签对象与目标提交对象形成一个关联。</li><li><strong>存储在对象数据库</strong>:标签对象连同其生成的哈希值会存储在Git对象数据库中,以确保其不可更改性和唯一性。<br>注释标签的哈希值可通过<code>git show <tag></code>查看。此哈希值唯一对应标签对象,而标签指向的提交对象则会有自己的哈希值。</li></ol><h4 id="标签对象的实现结构"><a href="#标签对象的实现结构" class="headerlink" title="标签对象的实现结构"></a>标签对象的实现结构</h4><p>标签对象的具体内容结构如下(可以通过<code>git cat-file -p <tag></code>查看):</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><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">object <commit-hash> # 指向的提交哈希</span><br><span class="line">type commit # 对象类型为commit</span><br><span class="line">tag <tag-name> # 标签的名称</span><br><span class="line">tagger <name> <email> <timestamp> <timezone> # 标签创建者的信息</span><br><span class="line"><message> # 标签注释</span><br></pre></td></tr></table></figure><p>这个标签对象和提交对象是相互独立的,它存储在<code>.git/objects/</code>目录中,单独占用一个哈希值。</p><h4 id="标签哈希值的作用"><a href="#标签哈希值的作用" class="headerlink" title="标签哈希值的作用"></a>标签哈希值的作用</h4><p>标签的哈希值可以确保每个标签的唯一性,尤其是在注释标签中,这个哈希值标识了标签对象的内容和元信息,确保了即便同名标签在内容不同情况下仍具有唯一性。这种机制使得Git标签成为项目版本控制中的不可变标记,为项目的发布和历史追溯提供了可靠依据。</p><h3 id="标签的创建与管理操作"><a href="#标签的创建与管理操作" class="headerlink" title="标签的创建与管理操作"></a>标签的创建与管理操作</h3><h4 id="1-创建轻量级标签"><a href="#1-创建轻量级标签" class="headerlink" title="1. 创建轻量级标签"></a>1. 创建轻量级标签</h4><p>轻量级标签只需指定标签名和要标记的提交即可,Git默认会将标签创建在<strong>当前的HEAD指针所指向的提交</strong>上(即当前分支的最新提交):</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag 标签名</span><br></pre></td></tr></table></figure><p>若需为指定的提交打标签,可以在命令后加上提交哈希:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag 标签名 提交哈希</span><br></pre></td></tr></table></figure><h4 id="2-创建注释标签"><a href="#2-创建注释标签" class="headerlink" title="2. 创建注释标签"></a>2. 创建注释标签</h4><p>注释标签带有更多信息,用 <code>-a</code> 参数创建,推荐为正式的发布版本添加注释标签。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -a 标签名 -m <span class="string">"标签注释"</span></span><br></pre></td></tr></table></figure><p>其中,<code>-m</code> 后的内容是标签的注释。</p><h4 id="3-查看标签"><a href="#3-查看标签" class="headerlink" title="3. 查看标签"></a>3. 查看标签</h4><p>用 <code>git tag</code> 查看当前仓库中所有的标签:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag</span><br></pre></td></tr></table></figure><p>可以使用 <code>git show 标签名</code> 查看指定标签的详细信息:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git show 标签名</span><br></pre></td></tr></table></figure><h4 id="4-删除标签"><a href="#4-删除标签" class="headerlink" title="4. 删除标签"></a>4. 删除标签</h4><p>删除标签时使用 <code>-d</code> 选项:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -d 标签名</span><br></pre></td></tr></table></figure><h4 id="5-推送标签到远程仓库"><a href="#5-推送标签到远程仓库" class="headerlink" title="5. 推送标签到远程仓库"></a>5. 推送标签到远程仓库</h4><p>标签默认不会自动推送到远程仓库,需要手动执行:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin 标签名</span><br></pre></td></tr></table></figure><p>推送所有标签到远程仓库:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin --tags</span><br></pre></td></tr></table></figure><h4 id="6-删除远程标签"><a href="#6-删除远程标签" class="headerlink" title="6. 删除远程标签"></a>6. 删除远程标签</h4><p>先删除本地标签,然后将删除操作推送到远程:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git tag -d 标签名</span><br><span class="line">git push origin :refs/tags/标签名</span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>Git标签为项目提供了灵活的版本管理方式,适合标记不同阶段的代码。轻量级标签简单快速,而注释标签提供了详细的版本信息,是常用的版本控制工具之一。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>本篇就是简单的介绍了一下Git的分支模型的实现原理,其实都是基于前两篇文章提到的不可变数据模型,经过前两篇文章的洗礼,这一篇理解起来应该没什么难度🫠。git分支本质是指针引用,指针的信息记录在<code>.git/ref</code>中,顺带的介绍了下tag。下一篇我将讲解Git的如何实现分布式合作了。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第二章(2):对象数据库</title>
<link href="/tangBlog/2024/11/03/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%882%EF%BC%89%EF%BC%9A%E5%AF%B9%E8%B1%A1%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
<url>/tangBlog/2024/11/03/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%882%EF%BC%89%EF%BC%9A%E5%AF%B9%E8%B1%A1%E6%95%B0%E6%8D%AE%E5%BA%93/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>理解了上篇文章的两大模型(快照和不可变对象)后,让我们看看Git 的核心——对象数据库,快照存储在 <code>.git/objects</code> 目录中,Git 通过这种方式管理项目的所有历史和数据。</p><h2 id="Git对象数据库"><a href="#Git对象数据库" class="headerlink" title="Git对象数据库"></a>Git对象数据库</h2><p>下面是 <code>.git/objects</code> 目录的基本结构:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">.git/objects/</span><br><span class="line">│</span><br><span class="line">├── e5/</span><br><span class="line">│ └── 0c5ab6d9c5e3ac9e7b1b3f1c3b2302c0f4db3 # 对象文件</span><br><span class="line">│</span><br><span class="line">├── 12/</span><br><span class="line">│ └── ab34b22f35e3f1c26c0c4e3dff9c3c0ff76c76a # 对象文件</span><br><span class="line">│</span><br><span class="line">├── 34/</span><br><span class="line">│ └── f8ae3f7b1bcad1cb38e3ae0a49dbd8b35a1be0b2 # 对象文件</span><br><span class="line">│</span><br><span class="line">├── pack/</span><br><span class="line">│ ├── pack-<hash>.pack # Pack文件</span><br><span class="line">│ └── pack-<hash>.idx # Pack索引文件</span><br><span class="line">│</span><br><span class="line">└── info/</span><br><span class="line"> └── packs # 存储有关已打包对象的信息</span><br></pre></td></tr></table></figure><p> <strong>说明:</strong></p><ul><li><code>e5/</code> 和 <code>12/</code> 等目录是根据对象SHA-1哈希值的前两位字符命名的,里面存储对应的对象文件。**</li><li>**<code>pack/</code> 目录包含打包的对象文件和相应的索引文件,用于优化存储和访问。</li><li><code>info/</code> 目录用于存储对象数据库的元信息,通常包括关于Pack文件的信息,以提高对象访问的效率。</li></ul><h2 id="高效存储"><a href="#高效存储" class="headerlink" title="高效存储"></a>高效存储</h2><p><strong>Git</strong> 采用快照存储模型,表面上看似每次提交都存储了项目的完整副本,但实际上 <strong>Git</strong> 在存储方面非常高效,通过多种技术手段优化了存储空间的使用,即使代码量大,Git 也不会占用过多空间:</p><h3 id="1-重复文件共享引用"><a href="#1-重复文件共享引用" class="headerlink" title="1. 重复文件共享引用"></a>1. <strong>重复文件共享引用</strong></h3><p>Git在每次提交时会为每个文件生成一个唯一的哈希值(SHA-1)。如果某个文件没有发生变化,Git不会为它创建新的副本,而是直接<strong>引用上一个版本的文件</strong>。因此,尽管Git每次记录的是项目的完整快照,但实际上对于未修改的文件,Git只是存储对之前版本的引用,不会重复存储相同的数据。<br><strong>示例</strong>:</p><ul><li>如果一个项目中有100个文件,但提交中只修改了3个,Git只会存储这3个修改过的文件的快照,其他97个文件会引用之前的快照。这极大节省了存储空间。</li></ul><h3 id="2-Zlib压缩存储"><a href="#2-Zlib压缩存储" class="headerlink" title="2. Zlib压缩存储"></a>2. <strong>Zlib压缩存储</strong></h3><p>Git在内部使用了高效的<strong>压缩算法</strong>(zlib)来处理文件的存储。Git会将文件内容压缩后再进行存储,这样即使是完整快照,它也会占用较少的空间。Git的压缩机制对于文本文件(如代码)尤其有效,可以显著减少存储空间的消耗。</p><h3 id="3-Pack文件"><a href="#3-Pack文件" class="headerlink" title="3.Pack文件"></a>3.Pack文件</h3><p>Git将提交、树、文件内容等对象存储在<code>.git/objects</code>目录中。随着时间推移,Git会将相似的对象<strong>打包成Pack文件</strong>存储在<code>.git/objects/pack</code>目录中,这是一种优化的存储格式,用来减少存储开销。Pack文件将多个对象进行<strong>增量压缩存储</strong>,使得整个仓库更紧凑,并进一步节省磁盘空间。</p><h3 id="4-Delta压缩(在Pack文件中)"><a href="#4-Delta压缩(在Pack文件中)" class="headerlink" title="4. Delta压缩(在Pack文件中)"></a>4. <strong>Delta压缩(在Pack文件中)</strong></h3><p>尽管Git不使用增量存储模型,但在<strong>Pack文件</strong>中,它会对文件内容进行<strong>增量压缩</strong>(类似于BitKeeper和CVS的方式)。在创建Pack文件时,Git会尝试寻找相似的文件内容并只存储它们之间的差异(delta),这样即使是快照模型,也会对相似的文件版本进行差异存储,进一步减少空间占用。</p><h3 id="5-轻量分支和标签"><a href="#5-轻量分支和标签" class="headerlink" title="5. 轻量分支和标签"></a>5. <strong>轻量分支和标签</strong></h3><p>Git的分支和标签实际上是<strong>轻量指针</strong>,它们只是指向具体的提交对象,并不需要额外存储代码内容。这意味着即使有多个分支和标签,Git也不会因此浪费大量空间,因为每个分支或标签只是引用已有的提交记录。</p><h3 id="6-垃圾回收(Git-GC)"><a href="#6-垃圾回收(Git-GC)" class="headerlink" title="6. 垃圾回收(Git GC)"></a>6. <strong>垃圾回收(Git GC)</strong></h3><p>Git有一个内置的<strong>垃圾回收机制</strong>(<code>git gc</code>),定期清理不再使用的对象,回收存储空间。比如,删除某些分支或进行其他操作后,Git会自动运行垃圾回收,移除无用的数据,进一步优化存储空间,这项工作有助于保持 <code>.git/objects</code> 目录的整洁,并释放存储空间。</p><h3 id="7-大文件管理:Git-LFS"><a href="#7-大文件管理:Git-LFS" class="headerlink" title="7. 大文件管理:Git LFS"></a>7. <strong>大文件管理:Git LFS</strong></h3><p>对于那些包含大量二进制文件或者非常大的文件(如图片、视频、数据集等)的大型项目,Git提供了<strong>Git LFS(Large File Storage)</strong>来管理这些大文件。Git LFS将大文件存储在外部的存储系统中,Git仓库只会存储指向这些文件的引用,从而避免了大文件占用过多的空间。</p><h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><ul><li><strong>高效引用机制</strong>:Git仅存储有变化的文件,并引用未修改的文件,减少了存储冗余。</li><li><strong>压缩与打包</strong>:Git使用压缩技术,并通过增量压缩和Pack文件进一步优化存储。</li><li><strong>轻量分支</strong>:Git的分支和标签是轻量的,不会显著增加存储空间。<br>因此,尽管Git采用快照模型,代码量增大时也不会占用过多存储空间。相比其他传统的版本控制系统(如Subversion和CVS),Git在管理大型代码库时依然具有出色的存储效率。<br>对git的高效存储有了大致的认识之后我们会对一些值得玩味的减少内存占用的操作(Zlib、Pack、Delta、GC、LFS)进行逐个分析,以求有更深刻的认识。</li></ul><h2 id="Zlib压缩存储"><a href="#Zlib压缩存储" class="headerlink" title="Zlib压缩存储"></a>Zlib压缩存储</h2><p>Git 使用 Zlib 压缩存储来优化存储和传输数据,尤其是在处理大型代码库时。以下是对 Zlib 压缩存储及其相关源代码的详细讲解:</p><h3 id="1-Zlib-压缩简介"><a href="#1-Zlib-压缩简介" class="headerlink" title="1. Zlib 压缩简介"></a>1. <strong>Zlib 压缩简介</strong></h3><p>Zlib 是一种数据压缩库,基于 DEFLATE 算法,这种算法结合了 LZ77 和 Huffman 编码,提供了良好的压缩比和速度。Git 利用 Zlib 对其对象(如提交、树、blob 和标签)进行压缩存储,以减少占用空间和提高传输效率。<br>DEFLATE 是一种无损压缩算法,意味着在解压缩时可以完美还原原始数据,这对 Git 中存储和传输版本控制数据至关重要。</p><h3 id="2-压缩流程"><a href="#2-压缩流程" class="headerlink" title="2. 压缩流程"></a>2. <strong>压缩流程</strong></h3><p>在 Git 中,压缩过程通常分为以下几个步骤:</p><ul><li><strong>对象创建</strong>:当你创建一个新对象(例如,提交或文件),Git 会首先将其内容转换为字符串,并计算其 SHA-1 哈希。</li><li><strong>压缩</strong>:然后,Git 使用 Zlib 对该对象进行压缩。压缩后的数据与哈希一起存储。</li><li><strong>存储</strong>:压缩后的对象存储在 <code>.git/objects</code> 目录中,以其哈希值的前两位作为目录名,剩下的部分作为文件名。</li></ul><h3 id="3-源代码分析"><a href="#3-源代码分析" class="headerlink" title="3. 源代码分析"></a>3. <strong>源代码分析</strong></h3><p>Git 的 Zlib 压缩主要涉及以下几个文件和函数:</p><ul><li>**<code>hash-object.c</code>**:负责将文件添加到对象存储的逻辑。</li><li>**<code>object.c</code>**:管理对象的创建和存储。</li><li>**<code>pack-objects.c</code>**:用于打包多个对象,利用 Zlib 进行压缩以减少空间。<br>以下是一些关键的函数和它们的作用:</li><li><code>zlib_deflate()</code>: 将数据压缩为 Zlib 格式。</li><li><code>zlib_inflate()</code>: 解压缩数据。</li><li><code>write_sha1_file()</code>: 将对象写入到对象存储,包括压缩和存储步骤。</li></ul><h3 id="4-示例"><a href="#4-示例" class="headerlink" title="4. 示例"></a>4. <strong>示例</strong></h3><p>以下是使用 Zlib 压缩的简单示例代码(不是 Git 的直接代码):</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><zlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">compress_data</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* input, <span class="type">size_t</span> input_length)</span> {</span><br><span class="line"> uLong compressed_length = compressBound(input_length);</span><br><span class="line"> <span class="type">char</span>* compressed_data = <span class="built_in">malloc</span>(compressed_length);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (compress((Bytef*)compressed_data, &compressed_length, (<span class="type">const</span> Bytef*)input, input_length) == Z_OK) {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Compressed size: %lu\n"</span>, compressed_length);</span><br><span class="line"> <span class="comment">// 处理压缩后的数据</span></span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">free</span>(compressed_data);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Zlib 被广泛应用于 Git 中,因为它提供了一种高效、灵活的压缩方案,能够与其他技术(如 Delta 编码)结合,优化数据存储和传输的整体效率。</p><h2 id="Pack文件"><a href="#Pack文件" class="headerlink" title="Pack文件"></a>Pack文件</h2><p><strong>自动打包</strong>是 Git 用于优化存储和提高性能的一项关键机制。随着时间推移,Git 仓库中的文件和对象会越来越多,特别是当项目有大量提交、分支、文件时,这些<strong>松散对象</strong>(单独存储的文件)会占据较多的磁盘空间并导致操作变慢。<br>为了应对这一问题,Git 会定期将这些松散对象打包成更紧凑的 <strong>Pack 文件</strong>,从而提高存储效率和访问速度。打包过程可以通过以下两种方式发生:</p><h3 id="1-Git的自动打包机制"><a href="#1-Git的自动打包机制" class="headerlink" title="1. Git的自动打包机制"></a>1. <strong>Git的自动打包机制</strong></h3><ul><li><strong>定期自动执行</strong>:Git在处理一些操作时,会在后台自动检查对象的数量和仓库的状态。当松散对象数量达到一定阈值后,Git会自动将这些对象打包为Pack文件。这种机制确保Git仓库随着时间推移保持紧凑,而无需用户干预。<br> 例如,当你频繁提交、拉取、推送、克隆等操作时,Git可能会自动触发打包操作。</li></ul><h3 id="2-打包过程"><a href="#2-打包过程" class="headerlink" title="2.打包过程"></a>2.<strong>打包过程</strong></h3><p>打包过程主要涉及以下几个步骤:</p><h4 id="a-选择对象"><a href="#a-选择对象" class="headerlink" title="a. 选择对象"></a>a. <strong>选择对象</strong></h4><ul><li>Git 首先选择要打包的对象。这些对象可以是自从上次打包以来新增的对象,也可以是历史版本中不再单独存储的对象。</li></ul><h4 id="b-压缩对象"><a href="#b-压缩对象" class="headerlink" title="b. 压缩对象"></a>b. <strong>压缩对象</strong></h4><ul><li><strong>使用 Delta 编码</strong>:在打包过程中,Git 还会检查选择的对象之间的相似性,并可能<strong>使用 Delta 编码</strong>来进一步减少存储空间。Delta 编码只存储对象之间的差异,而不是完整对象。</li><li><strong>最终压缩</strong>:为了提高效率,Git 会再次对打包后的数据使用 <strong>Zlib 压缩</strong>。这是因为 Delta 编码后的数据结构可能会导致较大的数据块,使用 Zlib 可以进一步压缩这些数据,减少最终的 packfile 大小。</li></ul><h4 id="c-创建-Packfile"><a href="#c-创建-Packfile" class="headerlink" title="c. 创建 Packfile"></a>c. <strong>创建 Packfile</strong></h4><ul><li>创建一个新的 packfile,将压缩后的对象写入其中。Git 会在 packfile 中使用特定的格式记录对象信息,包括对象类型、大小、SHA-1 哈希等。</li></ul><h4 id="d-更新索引"><a href="#d-更新索引" class="headerlink" title="d. 更新索引"></a>d. <strong>更新索引</strong></h4><ul><li>为了快速查找 packfile 中的对象,Git 会生成一个索引文件(<code>.idx</code>)。该文件记录了每个对象在 packfile 中的位置和元数据。</li></ul><h3 id="3-手动运行-git-gc-命令"><a href="#3-手动运行-git-gc-命令" class="headerlink" title="3. 手动运行 git gc 命令"></a>3. <strong>手动运行 <code>git gc</code> 命令</strong></h3><ul><li><p><strong><code>git gc</code>(garbage collection)</strong> 是 Git 的垃圾回收命令,它可以主动触发对象的打包和仓库清理。执行 <code>git gc</code> 时,Git 会:</p><ol><li><strong>打包松散对象</strong>:将单独存储的文件对象打包成更紧凑的 Pack 文件。</li><li><strong>清理无用数据</strong>:删除不再引用的对象、过时的提交、旧的日志文件等,回收存储空间。</li></ol><p> 运行 <code>git gc</code> 可以手动优化仓库,特别适用于仓库文件较大、历史提交较多的项目。你可以通过命令来强制执行垃圾回收</p><p> Git 会自动进行对象打包、清理,确保仓库保持良好的状态和高效的存储。</p></li></ul><h3 id="4-相关源代码"><a href="#4-相关源代码" class="headerlink" title="4. 相关源代码"></a>4. <strong>相关源代码</strong></h3><p>Git 的打包逻辑主要涉及以下几个文件和函数:</p><ul><li>**<code>pack-objects.c</code>**:负责管理对象的选择、压缩和打包过程。</li><li>**<code>packfile.h</code>**:定义与 packfile 相关的结构体和函数。<br>以下是一些关键函数:</li><li><code>pack_objects()</code>: 主要入口,负责选择和打包对象。</li><li><code>write_pack_file()</code>: 将对象写入到 packfile 中。</li><li><code>write_index_file()</code>: 生成索引文件。</li></ul><h3 id="5-示例代码"><a href="#5-示例代码" class="headerlink" title="5. 示例代码"></a>5. <strong>示例代码</strong></h3><p>以下是一个简化的示例,展示了如何使用 Zlib 压缩数据并写入 packfile(非 Git 源代码):</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><zlib.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">write_pack_file</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* data, <span class="type">size_t</span> data_length)</span> {</span><br><span class="line"> <span class="comment">// 压缩数据</span></span><br><span class="line"> uLong compressed_length = compressBound(data_length);</span><br><span class="line"> <span class="type">char</span>* compressed_data = <span class="built_in">malloc</span>(compressed_length);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (compress((Bytef*)compressed_data, &compressed_length, (<span class="type">const</span> Bytef*)data, data_length) == Z_OK) {</span><br><span class="line"> FILE *pack_file = fopen(<span class="string">"packfile.pack"</span>, <span class="string">"wb"</span>);</span><br><span class="line"> fwrite(compressed_data, <span class="number">1</span>, compressed_length, pack_file);</span><br><span class="line"> fclose(pack_file);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">free</span>(compressed_data);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="6-打包的优势"><a href="#6-打包的优势" class="headerlink" title="6.打包的优势"></a>6.<strong>打包的优势</strong></h3><ul><li><strong>减少文件数量</strong>:松散对象每个都占据单独的文件,随着提交的增多会导致文件系统中存在大量小文件,降低操作效率。打包将多个对象合并成一个或多个Pack文件,减少文件数量,提升文件系统的访问速度。</li><li><strong>节省空间</strong>:Pack文件会对相似的对象进行压缩存储,尤其是采用<strong>Delta压缩技术</strong>,使得仓库体积大大减少。</li><li><strong>提高性能</strong>:Pack文件结构紧凑,Git可以更快速地进行检索和读取操作,提升整体操作性能。</li></ul><h3 id="git-objects-pack"><a href="#git-objects-pack" class="headerlink" title=".git/objects/pack"></a><strong>.git/objects/pack</strong></h3><p>在执行了 <code>git gc</code> 后,Git 会将对象打包成 <strong>Pack文件</strong>,并存储在 <code>.git/objects/pack</code> 目录中。这个目录中的文件包括:</p><h4 id="1-Pack文件(-pack)"><a href="#1-Pack文件(-pack)" class="headerlink" title="1. Pack文件(.pack)"></a>1. <strong>Pack文件(.pack)</strong></h4><ul><li><strong>描述</strong>:Pack文件是打包好的Git对象集合,包含了提交对象(commit)、树对象(tree)、文件对象(blob)等。</li><li><strong>文件名格式</strong>:Pack文件的文件名以 <code>.pack</code> 结尾,文件名前面是一长串SHA-1哈希值,用来唯一标识这个Pack文件的内容。</li><li><strong>作用</strong>:Pack文件通过增量压缩存储相似的对象,以减少存储空间。它包含了实际的打包对象。<figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/objects/<span class="keyword">pack</span>/<span class="keyword">pack</span>-xxxxxx.pack</span><br></pre></td></tr></table></figure></li></ul><h4 id="2-索引文件(-idx)"><a href="#2-索引文件(-idx)" class="headerlink" title="2. 索引文件(.idx)"></a>2. <strong>索引文件(.idx)</strong></h4><ul><li><strong>描述</strong>:索引文件是与Pack文件配套的文件,它存储了Pack文件中各个对象的索引信息。</li><li><strong>文件名格式</strong>:与Pack文件相同,前面的哈希值相同,但扩展名为 <code>.idx</code>。</li><li><strong>作用</strong>:索引文件允许Git快速查找Pack文件中的对象。通过这个索引,Git不需要逐个解压所有对象,而是可以直接定位到特定对象的位置,提升查找效率。<br>例如:<figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/objects/<span class="keyword">pack</span>/<span class="keyword">pack</span>-xxxxxx.idx</span><br></pre></td></tr></table></figure></li></ul><h4 id="3-松散对象(Loose-Objects)"><a href="#3-松散对象(Loose-Objects)" class="headerlink" title="3. 松散对象(Loose Objects)"></a>3. <strong>松散对象(Loose Objects)</strong></h4><p>在打包之前,Git中的对象是以<strong>松散对象</strong>(Loose Objects)的形式存储的,每个对象存储为单独的文件。这些松散对象位于 <code>.git/objects/</code> 目录下,根据对象的SHA-1哈希值前两位创建子目录,例如:</p><figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.git/objects/ab/<span class="number">123456</span>...</span><br></pre></td></tr></table></figure><p>打包后,这些松散对象会被删除,只有Pack文件中的对象会被保留。如果你之前没见到Pack文件,可能是因为你的仓库还没有达到打包的条件,或者执行 <code>git gc</code> 后才生成了Pack文件。</p><h4 id="如何查看Pack文件"><a href="#如何查看Pack文件" class="headerlink" title="如何查看Pack文件"></a>如何查看Pack文件</h4><p>如果你想查看Pack文件中的内容,可以使用以下Git命令:</p><ol><li><strong>列出Pack文件内容</strong>:</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git verify-pack -v .git/objects/pack/pack-xxxxxx.idx</span><br></pre></td></tr></table></figure><pre><code>这个命令会显示Pack文件中的所有对象及其大小、类型等信息。</code></pre><ol start="2"><li><strong>解压Pack文件</strong>: 如果你想解压Pack文件查看其中的对象,可以使用:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git unpack-objects < .git/objects/pack/pack-xxxxxx.pack</span><br></pre></td></tr></table></figure> 这个命令会解压出所有Pack文件中的对象,重新存储为松散对象。</li></ol><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>在Git中,Pack文件及其索引文件是为了优化存储和查找效率而引入的。如果你没有执行 <code>git gc</code> 或者仓库没有达到一定规模,可能没有注意到它们的存在。Pack文件将仓库的对象打包并压缩,有助于提高Git的性能,尤其是在处理大量对象时。</p><h2 id="Delta压缩"><a href="#Delta压缩" class="headerlink" title="Delta压缩"></a>Delta压缩</h2><ul><li>在 Git 的具体实现中,Zlib 和 Delta 编码可以结合使用。Git 首先会使用 Zlib 压缩单个对象,然后在打包过程中,如果有相似的对象,它会考虑将它们的差异存储为 Delta,以进一步节省空间。</li><li>当你从远程仓库拉取时,Git 会使用 Delta 编码来减少网络传输的数据量,而在本地存储时,它会使用压缩后的 packfile。<br>Git 中的 Delta 压缩是一种用于存储对象之间差异的技术,旨在进一步优化存储空间。以下是对 Delta 压缩的详细讲解以及相关的源码分析。</li></ul><h3 id="1-Delta-压缩的概念"><a href="#1-Delta-压缩的概念" class="headerlink" title="1. Delta 压缩的概念"></a>1. <strong>Delta 压缩的概念</strong></h3><ul><li>Delta 压缩(差分压缩)用于存储对象之间的差异,而不是完整的对象。对于相似或相同的对象,这可以显著减少所需的存储空间。</li><li>在 Git 中,通常用于 blob 对象(文件内容)和 tree 对象(目录结构)。</li></ul><h3 id="2-Delta-压缩的工作流程"><a href="#2-Delta-压缩的工作流程" class="headerlink" title="2. Delta 压缩的工作流程"></a>2. <strong>Delta 压缩的工作流程</strong></h3><h4 id="a-选择基准对象"><a href="#a-选择基准对象" class="headerlink" title="a. 选择基准对象"></a>a. <strong>选择基准对象</strong></h4><ul><li>在进行 Delta 压缩时,Git 选择一个“基准”对象(通常是一个较旧的版本)作为比较对象。</li></ul><h4 id="b-生成-Delta"><a href="#b-生成-Delta" class="headerlink" title="b. 生成 Delta"></a>b. <strong>生成 Delta</strong></h4><ul><li>Git 使用一种算法计算当前对象与基准对象之间的差异。这个过程包括:<ul><li>查找相同部分(称为 “context”)。</li><li>记录插入、删除和替换操作,以生成 delta 数据。</li></ul></li></ul><h4 id="c-存储-Delta"><a href="#c-存储-Delta" class="headerlink" title="c. 存储 Delta"></a>c. <strong>存储 Delta</strong></h4><ul><li>Delta 数据会与基准对象一起存储,Git 在解压缩时使用基准对象来重建原始对象。</li></ul><h3 id="3-Delta-压缩的源码"><a href="#3-Delta-压缩的源码" class="headerlink" title="3. Delta 压缩的源码"></a>3. <strong>Delta 压缩的源码</strong></h3><p>Git 的 Delta 压缩主要涉及以下几个文件和函数:</p><h4 id="a-关键文件"><a href="#a-关键文件" class="headerlink" title="a. 关键文件"></a>a. <strong>关键文件</strong></h4><ul><li>**<code>delta.c</code>**:实现 Delta 编码和解码的核心逻辑。</li><li>**<code>pack-objects.c</code>**:负责选择对象并管理打包过程,包括 Delta 压缩。</li></ul><h4 id="b-重要函数"><a href="#b-重要函数" class="headerlink" title="b. 重要函数"></a>b. <strong>重要函数</strong></h4><ul><li>**<code>create_delta()</code>**:用于生成 delta 数据的主要函数。</li><li>**<code>apply_delta()</code>**:用于将 delta 应用到基准对象以重建原始对象。</li></ul><h4 id="c-示例代码"><a href="#c-示例代码" class="headerlink" title="c. 示例代码"></a>c. <strong>示例代码</strong></h4><p>以下是一个简化的示例,展示如何在 Git 中生成和应用 delta(不是 Git 的实际实现):</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><string.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">create_delta</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* base, <span class="type">const</span> <span class="type">char</span>* target, <span class="type">char</span>* delta)</span> {</span><br><span class="line"> <span class="comment">// 简化的 delta 生成逻辑,实际逻辑会复杂得多</span></span><br><span class="line"> <span class="comment">// 假设 delta 仅包含插入和删除操作</span></span><br><span class="line"> <span class="type">int</span> base_length = <span class="built_in">strlen</span>(base);</span><br><span class="line"> <span class="type">int</span> target_length = <span class="built_in">strlen</span>(target);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 比较两个字符串,生成 delta</span></span><br><span class="line"> <span class="comment">// 这里只是示例逻辑</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < target_length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (i >= base_length || base[i] != target[i]) {</span><br><span class="line"> delta[i] = target[i]; <span class="comment">// 插入或替换</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">apply_delta</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* base, <span class="type">const</span> <span class="type">char</span>* delta, <span class="type">char</span>* result)</span> {</span><br><span class="line"> <span class="comment">// 将 delta 应用到基准对象</span></span><br><span class="line"> <span class="built_in">strcpy</span>(result, base);</span><br><span class="line"> <span class="comment">// 假设 delta 仅包含插入操作</span></span><br><span class="line"> <span class="built_in">strcat</span>(result, delta);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span>* base = <span class="string">"Hello World!"</span>;</span><br><span class="line"> <span class="type">const</span> <span class="type">char</span>* target = <span class="string">"Hello Git World!"</span>;</span><br><span class="line"> <span class="type">char</span> delta[<span class="number">100</span>];</span><br><span class="line"> <span class="type">char</span> result[<span class="number">100</span>];</span><br><span class="line"></span><br><span class="line"> create_delta(base, target, delta);</span><br><span class="line"> apply_delta(base, delta, result);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Base: %s\n"</span>, base);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Delta: %s\n"</span>, delta);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Result: %s\n"</span>, result);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="4-总结"><a href="#4-总结" class="headerlink" title="4. 总结"></a>4. <strong>总结</strong></h3><p>Git 的 Delta 压缩技术通过存储对象之间的差异,显著减少了存储空间的需求。其实现结合了对象选择、差异计算和有效存储策略。</p><h2 id="垃圾回收机制(git-gc)"><a href="#垃圾回收机制(git-gc)" class="headerlink" title="垃圾回收机制(git gc)"></a>垃圾回收机制(<code>git gc</code>)</h2><p>Git 中的垃圾回收机制(<code>git gc</code>)旨在优化存储,清理不再需要的对象,并合并多个对象以减少磁盘空间的占用。下面是对垃圾回收机制的详细讲解以及相关的源码分析。</p><h3 id="1-垃圾回收的概念"><a href="#1-垃圾回收的概念" class="headerlink" title="1. 垃圾回收的概念"></a>1. <strong>垃圾回收的概念</strong></h3><ul><li>Git 在运行时会生成许多对象(例如提交、blob、tree 和标签),这些对象在某些情况下可能不再被引用(即没有被任何分支或标签指向)。</li><li>垃圾回收的目的就是清理这些未引用的对象,从而释放存储空间。</li></ul><h3 id="2-git-gc-命令"><a href="#2-git-gc-命令" class="headerlink" title="2. git gc 命令"></a>2. <strong><code>git gc</code> 命令</strong></h3><ul><li><code>git gc</code> 是执行垃圾回收的命令。其主要功能包括:<ul><li><strong>删除</strong>未引用的对象。</li><li><strong>压缩和打包</strong>对象,以减少磁盘占用。</li><li><strong>更新</strong>引用和索引文件。</li></ul></li></ul><h3 id="3-垃圾回收的工作流程"><a href="#3-垃圾回收的工作流程" class="headerlink" title="3. 垃圾回收的工作流程"></a>3. <strong>垃圾回收的工作流程</strong></h3><h4 id="a-发现未引用对象"><a href="#a-发现未引用对象" class="headerlink" title="a. 发现未引用对象"></a>a. <strong>发现未引用对象</strong></h4><ul><li>Git 首先扫描对象库,找出所有未被引用的对象。这些对象包括:<ul><li>存在于 <code>.git/objects</code> 目录中的对象,但不再被任何分支、标签或其他引用所指向。</li></ul></li></ul><h4 id="b-删除未引用对象"><a href="#b-删除未引用对象" class="headerlink" title="b. 删除未引用对象"></a>b. <strong>删除未引用对象</strong></h4><ul><li>Git 会删除这些未引用的对象,以释放存储空间。</li></ul><h4 id="c-打包对象"><a href="#c-打包对象" class="headerlink" title="c. 打包对象"></a>c. <strong>打包对象</strong></h4><ul><li>Git 会将多个对象打包成一个或多个 packfile,以提高存储效率。这个过程会使用 Delta 压缩来进一步减少占用空间。</li></ul><h3 id="4-相关源码"><a href="#4-相关源码" class="headerlink" title="4. 相关源码"></a>4. <strong>相关源码</strong></h3><p>Git 的垃圾回收机制主要涉及以下几个文件和函数:</p><h4 id="a-关键文件-1"><a href="#a-关键文件-1" class="headerlink" title="a. 关键文件"></a>a. <strong>关键文件</strong></h4><ul><li>**<code>gc.c</code>**:实现垃圾回收的核心逻辑。</li><li>**<code>object.c</code>**:管理对象的创建、查找和删除。</li><li>**<code>pack-objects.c</code>**:负责对象的打包和压缩。</li></ul><h4 id="b-重要函数-1"><a href="#b-重要函数-1" class="headerlink" title="b. 重要函数"></a>b. <strong>重要函数</strong></h4><ul><li>**<code>git_gc()</code>**:执行垃圾回收的主要函数。</li><li>**<code>prune_objects()</code>**:查找并删除未引用的对象。</li><li>**<code>pack_objects()</code>**:将对象打包成 packfile。</li></ul><h3 id="5-示例代码-1"><a href="#5-示例代码-1" class="headerlink" title="5. 示例代码"></a>5. <strong>示例代码</strong></h3><p>以下是一个简化的示例,展示了如何在 Git 中实现垃圾回收的基本逻辑(非 Git 的实际实现):</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdlib.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">prune_objects</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 简化的未引用对象删除逻辑</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Pruning unreachable objects...\n"</span>);</span><br><span class="line"> <span class="comment">// 实际逻辑会遍历对象库,删除未引用对象</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">pack_objects</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 简化的对象打包逻辑</span></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Packing objects...\n"</span>);</span><br><span class="line"> <span class="comment">// 实际逻辑会将多个对象压缩并存储为一个 packfile</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">git_gc</span><span class="params">()</span> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Starting garbage collection...\n"</span>);</span><br><span class="line"> prune_objects();</span><br><span class="line"> pack_objects();</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Garbage collection completed.\n"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> {</span><br><span class="line"> git_gc();</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="6-相关指令"><a href="#6-相关指令" class="headerlink" title="6.相关指令"></a>6.相关指令</h3><h4 id="1-git-gc"><a href="#1-git-gc" class="headerlink" title="1. git gc"></a>1. <strong><code>git gc</code></strong></h4><ul><li><strong>用途</strong>:执行垃圾回收,清理未引用的对象,压缩并打包对象。</li><li><strong>用法</strong>:直接在终端运行 <code>git gc</code>。</li></ul><h4 id="2-git-prune"><a href="#2-git-prune" class="headerlink" title="2. git prune"></a>2. <strong><code>git prune</code></strong></h4><ul><li><strong>用途</strong>:删除未被任何引用所指向的对象。这个命令通常是 <code>git gc</code> 的一部分,但可以单独运行。</li><li><strong>用法</strong>:直接在终端运行 <code>git prune</code>。</li></ul><h4 id="3-git-repack"><a href="#3-git-repack" class="headerlink" title="3. git repack"></a>3. <strong><code>git repack</code></strong></h4><ul><li><strong>用途</strong>:重新打包已经存在的 packfile,将多个 packfile 合并成一个,并进行压缩。可以通过选项来指定行为。</li><li><strong>用法</strong>:<code>git repack</code>,可以加上不同的选项,例如 <code>-a</code>(打包所有对象)或 <code>-d</code>(删除旧的 packfile)。</li></ul><h4 id="4-git-fsck"><a href="#4-git-fsck" class="headerlink" title="4. git fsck"></a>4. <strong><code>git fsck</code></strong></h4><ul><li><strong>用途</strong>:检查 Git 仓库的完整性,找出丢失的对象和不一致的引用。在进行垃圾回收之前,通常会建议先运行这个命令。</li><li><strong>用法</strong>:直接在终端运行 <code>git fsck</code>。</li></ul><h4 id="5-git-config"><a href="#5-git-config" class="headerlink" title="5. git config"></a>5. <strong><code>git config</code></strong></h4><ul><li><strong>用途</strong>:可以配置 Git 的垃圾回收行为,例如设置自动垃圾回收的时间间隔。</li><li><strong>用法</strong>:<ul><li><code>git config --global gc.auto <n></code>:设置当对象数量超过 <code>n</code> 时自动运行 <code>git gc</code>。</li><li><code>git config --global gc.autoPackLimit <n></code>:设置触发自动打包的对象数量限制。</li></ul></li></ul><h3 id="7-总结"><a href="#7-总结" class="headerlink" title="7. 总结"></a>7. <strong>总结</strong></h3><p>Git 的垃圾回收机制通过清理未引用对象和打包对象来优化存储,确保磁盘空间得到有效利用。</p><h2 id="Git-LFS"><a href="#Git-LFS" class="headerlink" title="Git LFS"></a>Git LFS</h2><p>Git LFS(Large File Storage)是一个用于管理 Git 中大文件的扩展,解决了 Git 本身在处理大文件时的性能问题。下面是对 Git LFS 的详细讲解,包括其使用方法和相关源码分析。</p><h3 id="1-Git-LFS-的概念"><a href="#1-Git-LFS-的概念" class="headerlink" title="1. Git LFS 的概念"></a>1. <strong>Git LFS 的概念</strong></h3><ul><li>Git LFS 旨在处理大文件(如图像、音频、视频等),避免将它们直接存储在 Git 仓库中,这样可以减少 Git 仓库的大小,提高操作速度。</li><li>LFS 将大文件的实际内容存储在外部服务器上,而在 Git 仓库中只保留指向这些文件的指针。</li></ul><h3 id="2-Git-LFS-的工作流程"><a href="#2-Git-LFS-的工作流程" class="headerlink" title="2. Git LFS 的工作流程"></a>2. <strong>Git LFS 的工作流程</strong></h3><h4 id="a-安装-Git-LFS"><a href="#a-安装-Git-LFS" class="headerlink" title="a. 安装 Git LFS"></a>a. <strong>安装 Git LFS</strong></h4><ul><li>在使用 Git LFS 之前,首先需要安装它。可以通过以下命令进行安装:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git lfs install</span><br></pre></td></tr></table></figure></li></ul><h4 id="b-跟踪大文件"><a href="#b-跟踪大文件" class="headerlink" title="b. 跟踪大文件"></a>b. <strong>跟踪大文件</strong></h4><ul><li>使用 <code>git lfs track</code> 命令来指定需要使用 LFS 管理的大文件类型。例如:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git lfs track <span class="string">"*.psd"</span></span><br></pre></td></tr></table></figure></li><li>这将创建或更新 <code>.gitattributes</code> 文件,指明哪些文件类型应该使用 LFS。</li></ul><h4 id="c-添加和提交大文件"><a href="#c-添加和提交大文件" class="headerlink" title="c. 添加和提交大文件"></a>c. <strong>添加和提交大文件</strong></h4><ul><li>将大文件添加到 Git 仓库时,使用普通的 Git 命令:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git add mylargefile.psd</span><br><span class="line">git commit -m <span class="string">"Add large file"</span></span><br></pre></td></tr></table></figure></li></ul><h4 id="d-推送和拉取"><a href="#d-推送和拉取" class="headerlink" title="d. 推送和拉取"></a>d. <strong>推送和拉取</strong></h4><ul><li>当你推送更改时,大文件的内容会被上传到 LFS 服务器,而 Git 仓库中只会存储指向这些大文件的指针。</li><li>拉取时,Git LFS 会自动下载这些大文件。</li></ul><h3 id="3-Git-LFS-的源码"><a href="#3-Git-LFS-的源码" class="headerlink" title="3. Git LFS 的源码"></a>3. <strong>Git LFS 的源码</strong></h3><p>Git LFS 是一个独立的工具,包含多个关键组件和功能。以下是 Git LFS 的一些核心源码概念:</p><h4 id="a-关键组件"><a href="#a-关键组件" class="headerlink" title="a. 关键组件"></a>a. <strong>关键组件</strong></h4><ul><li><strong>Pointer File</strong>:LFS 用于替代大文件的指针文件,包含指向实际大文件的元数据(如文件的哈希和大小)。</li><li><strong>Storage Backend</strong>:负责存储和检索大文件,可以是本地或远程存储。</li></ul><h4 id="b-重要函数-2"><a href="#b-重要函数-2" class="headerlink" title="b. 重要函数"></a>b. <strong>重要函数</strong></h4><ul><li><strong>Track Files</strong>:用于更新 <code>.gitattributes</code> 文件的函数。</li><li><strong>Upload</strong>:将大文件上传到 LFS 存储的函数。</li><li><strong>Download</strong>:从 LFS 存储下载大文件的函数。</li></ul><h4 id="c-示例代码-1"><a href="#c-示例代码-1" class="headerlink" title="c. 示例代码"></a>c. <strong>示例代码</strong></h4><p>以下是一个简化的示例,展示如何使用 LFS 处理大文件(非 Git LFS 的实际实现):</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">LFS</span>:</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> self.storage = {}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">track</span>(<span class="params">self, file_pattern</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">f"Tracking files: <span class="subst">{file_pattern}</span>"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">upload</span>(<span class="params">self, file_path</span>):</span><br><span class="line"> <span class="comment"># 假设上传文件并返回指针信息</span></span><br><span class="line"> file_hash = <span class="built_in">hash</span>(file_path) <span class="comment"># 简化的哈希生成</span></span><br><span class="line"> self.storage[file_hash] = file_path</span><br><span class="line"> <span class="keyword">return</span> <span class="string">f"Pointer for <span class="subst">{file_path}</span> stored with hash <span class="subst">{file_hash}</span>"</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">download</span>(<span class="params">self, file_hash</span>):</span><br><span class="line"> <span class="keyword">return</span> self.storage.get(file_hash, <span class="string">"File not found"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 示例使用</span></span><br><span class="line">lfs = LFS()</span><br><span class="line">lfs.track(<span class="string">"*.psd"</span>)</span><br><span class="line">pointer = lfs.upload(<span class="string">"mylargefile.psd"</span>)</span><br><span class="line"><span class="built_in">print</span>(pointer)</span><br><span class="line">file_content = lfs.download(<span class="built_in">hash</span>(<span class="string">"mylargefile.psd"</span>))</span><br><span class="line"><span class="built_in">print</span>(file_content)</span><br></pre></td></tr></table></figure><h3 id="4-总结-1"><a href="#4-总结-1" class="headerlink" title="4. 总结"></a>4. <strong>总结</strong></h3><p>Git LFS 提供了一种高效的方式来管理大文件,避免了将它们直接存储在 Git 仓库中,从而提升了仓库的性能和可管理性。</p><h2 id="回看-git文件"><a href="#回看-git文件" class="headerlink" title="回看.git文件"></a>回看.git文件</h2><h2 id="git-文件夹存储了整个项目的版本历史和所有提交记录。也就是说,这个文件夹中包含了项目的完整变更记录、提交的快照、分支信息等内容。只要-git-文件夹保留完整,你可以通过-git-checkout-或-git-reset-等命令,恢复到项目的任何一个历史版本,甚至恢复整个项目的文件内容。-git文件夹-git-文件夹的主要内容包括:1-objects-文件夹:存储了每个提交的对象(包括提交对象、树对象、和-blob-对象),这些对象通过哈希值(commit-ID)来索引,确保文件内容不会重复存储。2-refs-文件夹:保存了指向分支、标签的指针,帮助-Git-了解当前分支的-HEAD-指针等信息。3-HEAD-文件:用于指向当前检出的分支,告诉-Git-哪个提交是当前版本。因此,-git-文件夹可以看成是一个精简的数据库,记录了项目的所有历史状态和操作记录。如果你复制-git-文件夹到另一个目录,并执行-git-checkout,就可以恢复出项目的完整文件夹结构和内容。"><a href="#git-文件夹存储了整个项目的版本历史和所有提交记录。也就是说,这个文件夹中包含了项目的完整变更记录、提交的快照、分支信息等内容。只要-git-文件夹保留完整,你可以通过-git-checkout-或-git-reset-等命令,恢复到项目的任何一个历史版本,甚至恢复整个项目的文件内容。-git文件夹-git-文件夹的主要内容包括:1-objects-文件夹:存储了每个提交的对象(包括提交对象、树对象、和-blob-对象),这些对象通过哈希值(commit-ID)来索引,确保文件内容不会重复存储。2-refs-文件夹:保存了指向分支、标签的指针,帮助-Git-了解当前分支的-HEAD-指针等信息。3-HEAD-文件:用于指向当前检出的分支,告诉-Git-哪个提交是当前版本。因此,-git-文件夹可以看成是一个精简的数据库,记录了项目的所有历史状态和操作记录。如果你复制-git-文件夹到另一个目录,并执行-git-checkout,就可以恢复出项目的完整文件夹结构和内容。" class="headerlink" title=".git 文件夹存储了整个项目的版本历史和所有提交记录。也就是说,这个文件夹中包含了项目的完整变更记录、提交的快照、分支信息等内容。只要 .git 文件夹保留完整,你可以通过 git checkout 或 git reset 等命令,恢复到项目的任何一个历史版本,甚至恢复整个项目的文件内容。### .git文件夹.git 文件夹的主要内容包括:1. objects 文件夹:存储了每个提交的对象(包括提交对象、树对象、和 blob 对象),这些对象通过哈希值(commit ID)来索引,确保文件内容不会重复存储。2. refs 文件夹:保存了指向分支、标签的指针,帮助 Git 了解当前分支的 HEAD 指针等信息。3. HEAD 文件:用于指向当前检出的分支,告诉 Git 哪个提交是当前版本。因此,.git 文件夹可以看成是一个精简的数据库,记录了项目的所有历史状态和操作记录。如果你复制 .git 文件夹到另一个目录,并执行 git checkout,就可以恢复出项目的完整文件夹结构和内容。"></a><code>.git</code> 文件夹存储了整个项目的版本历史和所有提交记录。也就是说,这个文件夹中包含了项目的完整变更记录、提交的快照、分支信息等内容。只要 <code>.git</code> 文件夹保留完整,你可以通过 <code>git checkout</code> 或 <code>git reset</code> 等命令,恢复到项目的任何一个历史版本,甚至恢复整个项目的文件内容。<br>### .git文件夹<br><code>.git</code> 文件夹的主要内容包括:<br>1. <strong>objects 文件夹</strong>:存储了每个提交的对象(包括提交对象、树对象、和 blob 对象),这些对象通过哈希值(commit ID)来索引,确保文件内容不会重复存储。<br>2. <strong>refs 文件夹</strong>:保存了指向分支、标签的指针,帮助 Git 了解当前分支的 HEAD 指针等信息。<br>3. <strong>HEAD 文件</strong>:用于指向当前检出的分支,告诉 Git 哪个提交是当前版本。<br>因此,<code>.git</code> 文件夹可以看成是一个精简的数据库,记录了项目的所有历史状态和操作记录。如果你复制 <code>.git</code> 文件夹到另一个目录,并执行 <code>git checkout</code>,就可以恢复出项目的完整文件夹结构和内容。</h2><p>复制 <code>.git</code> 文件夹后,可以按照以下步骤来确保项目文件正确恢复:</p><ol><li><strong>进入目标目录</strong>:在新目录下,确认 <code>.git</code> 文件夹已经被正确复制到根目录。</li><li><strong>恢复所有文件</strong>:<ul><li>运行以下命令,将工作区恢复到最新的提交状态:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout HEAD -- .</span><br></pre></td></tr></table></figure>这条命令会将 <code>.git</code> 文件夹中的记录应用到工作区,恢复项目的所有文件。</li></ul></li><li><strong>验证恢复的文件</strong>:你可以使用 <code>git status</code> 查看文件状态,确认所有文件已恢复至项目的最新提交版本。<br>这种方法利用 <code>.git</code> 文件夹的提交记录来重新生成项目的完整文件结构,是一种手动的恢复方式。这种恢复操作的前提是,<code>.git</code> 文件夹内所有提交记录都是完整的。</li></ol><h3 id="版本回退"><a href="#版本回退" class="headerlink" title="版本回退"></a>版本回退</h3><p>如果你想回退到之前某一次的提交状态,可以使用以下几种方法,具体选择取决于你是否希望保留回退后的更改记录。</p><h4 id="1-使用-git-checkout-(回到指定提交查看文件,但不修改提交历史)"><a href="#1-使用-git-checkout-(回到指定提交查看文件,但不修改提交历史)" class="headerlink" title="1. 使用 git checkout <commit>(回到指定提交查看文件,但不修改提交历史)"></a>1. 使用 <code>git checkout <commit></code>(回到指定提交查看文件,但不修改提交历史)</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout <commit_hash></span><br></pre></td></tr></table></figure><ul><li>这里的 <code><commit_hash></code> 是你想要回退到的那个提交的哈希值。</li><li>这种方式会让工作区进入“分离头指针”(detached HEAD)状态,你可以查看那个版本的文件内容,但此时不会更新分支的最新提交记录。</li><li>如果你只是想临时查看某个历史版本,这个方法非常适合。要回到最新的提交,可以运行 <code>git checkout main</code> 或 <code>git checkout <branch_name></code> 切换回原来的分支。</li></ul><h4 id="2-使用-git-reset(更改分支历史)"><a href="#2-使用-git-reset(更改分支历史)" class="headerlink" title="2. 使用 git reset(更改分支历史)"></a>2. 使用 <code>git reset</code>(更改分支历史)</h4><p><code>git reset</code> 有不同的选项,具体取决于你是否要保留当前工作区的更改:<br><strong>回退并保留更改 (<code>--soft</code>)</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset --soft <commit_hash></span><br></pre></td></tr></table></figure><ul><li><code>--soft</code> 会将 HEAD 移动到指定的提交,但会保留之后的更改,并将这些更改保存在暂存区中,方便你再次提交。<br><strong>回退并丢弃暂存区的更改 (<code>--mixed</code>)</strong><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset --mixed <commit_hash></span><br></pre></td></tr></table></figure></li><li><code>--mixed</code> 是默认选项,它会回退到指定提交,并将之后的更改从暂存区中移除,但会保留在工作区中,这样你可以再次进行调整。<br><strong>回退并丢弃所有更改 (<code>--hard</code>)</strong><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset --hard <commit_hash></span><br></pre></td></tr></table></figure></li><li><code>--hard</code> 会完全回退到指定的提交,之后的所有更改都会被清除,不再保留。</li></ul><h4 id="3-使用-git-revert(回退特定提交,保留历史)"><a href="#3-使用-git-revert(回退特定提交,保留历史)" class="headerlink" title="3. 使用 git revert(回退特定提交,保留历史)"></a>3. 使用 <code>git revert</code>(回退特定提交,保留历史)</h4><p>如果你想回到之前的状态,但不希望丢失提交记录,可以使用 <code>git revert</code>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git revert <commit_hash></span><br></pre></td></tr></table></figure><ul><li><code>git revert</code> 会创建一个新的提交,撤销指定的提交内容,但保留原有的提交历史。这样既能恢复到之前的状态,也不会破坏历史记录,适用于团队协作。</li></ul><h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><ul><li><strong>临时查看旧版本</strong>:<code>git checkout <commit_hash></code></li><li><strong>修改分支历史</strong>:<code>git reset</code>(根据需要选择 <code>--soft</code>、<code>--mixed</code> 或 <code>--hard</code>)</li><li><strong>保持提交记录,创建回退提交</strong>:<code>git revert</code><br>这些方法可以帮助你在不同时期的提交之间灵活回退和恢复。</li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>呼,这篇文章确实有点干,但是我写下来也是收获满满,只是不知道有什么用🫠<br>主要介绍了下Git的对象数据库<code>.git/object</code>文件夹和其相关一系列的减少内存占用的设计。<br>下一篇估计是会介绍git的分支,还没想好具体要涵盖什么知识点。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第二章(1):快照和不可变对象模型</title>
<link href="/tangBlog/2024/11/03/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%881%EF%BC%89%EF%BC%9A%E5%BF%AB%E7%85%A7%E5%92%8C%E4%B8%8D%E5%8F%AF%E5%8F%98%E5%AF%B9%E8%B1%A1%E6%A8%A1%E5%9E%8B/"/>
<url>/tangBlog/2024/11/03/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%BA%8C%E7%AB%A0%EF%BC%881%EF%BC%89%EF%BC%9A%E5%BF%AB%E7%85%A7%E5%92%8C%E4%B8%8D%E5%8F%AF%E5%8F%98%E5%AF%B9%E8%B1%A1%E6%A8%A1%E5%9E%8B/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>上一篇文章主要介绍些Git起源背后的一些故事背景,从这篇开始将逐渐讲解Git的设计理念,包括分布式控制、快照管理、不可变对象模型和分支模型。其实上述概念都不是孤立的,在讲解中会发现它们是相辅相成的有机整体,实现1+1大于2的效果。<br>接下来预计会按照<strong>快照模型与不可变对象模型</strong>、<strong>分支模型</strong>、<strong>分布式控制</strong>这样的顺序讲解,其用意到后面自会明了。<br>本人总是从中能看到区块链的影子,之后会引入区块链的相关知识进行补充,也当是拓展了🫠。</p><h2 id="快照存储模型"><a href="#快照存储模型" class="headerlink" title="快照存储模型"></a>快照存储模型</h2><p>快照模型与传统的版本控制系统(如SVN等)不同。传统系统通常基于文件的差异(diff)来管理版本,而Git则使用快照(snapshot)的方式来记录项目的每个版本。</p><h3 id="快照模型的主要特点:"><a href="#快照模型的主要特点:" class="headerlink" title="快照模型的主要特点:"></a>快照模型的主要特点:</h3><ol><li><strong>快照而非差异</strong>:<ul><li>Git会在每次提交时保存项目的当前状态,称为“快照”。这意味着每次提交都是整个项目的完整状态,而不是仅保存变化部分。</li></ul></li><li><strong>只保存变更</strong>:<ul><li>虽然每个提交实际上是一个完整的快照,但Git为了节省空间,内部实现上只存储更改过的文件的内容,而未改变的文件只保存一次。这使得Git非常高效。</li></ul></li><li><strong>对象存储</strong>:<ul><li>Git将文件和目录的快照存储为对象,每个对象都有一个唯一的SHA-1哈希值。这种方式不仅提供了数据的完整性,还能有效管理版本。</li></ul></li><li><strong>树(Tree)和Blob</strong>:<ul><li>在Git中,目录结构被称为“树”(tree),每个文件被称为“Blob”(binary large object)。每次提交都会创建一个新的树对象,指向当前提交的文件状态。</li></ul></li><li><strong>提交(Commit)对象</strong>:<ul><li>每次提交不仅包含快照,还包含提交信息、作者信息、时间戳和指向父提交的指针。这使得Git可以轻松地追踪历史和分支。</li></ul></li><li><strong>高效的分支管理</strong>:<ul><li>因为每个提交都是一个独立的快照,创建分支实际上只需要创建一个新的指针,指向当前的提交。这使得分支操作非常轻量级,极大地方便了开发过程中的实验和合并。</li></ul></li></ol><h2 id="不可变对象模型"><a href="#不可变对象模型" class="headerlink" title="不可变对象模型"></a>不可变对象模型</h2><p>其实这就是上述在快照模型中提到Git对象数据库的具体展开,主要就是三大对象类型结合SHA-1来保证数据的不可变性。</p><blockquote><p>Git的内部数据结构依赖于 **[[加密哈希函数|SHA-1]]**,为每个对象(如文件和提交)生成唯一标识。这种设计确保了数据的不可变性:</p><ul><li><strong>数据完整性</strong>:每个对象的内容会被哈希处理,任何对内容的修改都会导致哈希值的变化,从而提示开发者数据已被更改。</li><li><strong>一致性</strong>:由于对象的内容与其哈希值紧密关联,Git能够保证版本库的一致性,防止数据丢失或损坏。这种机制使得任何用户都可以安全地信任本地的Git仓库。</li></ul></blockquote><p>Git中的哈希值通过对对象内容和元数据进行特定格式的组合,然后应用SHA-1哈希函数生成。这个过程确保了每个对象都有唯一的标识符,同时也保证了数据的完整性和安全性。我们先来看下它们的具体的步骤和原理:</p><h3 id="三大对象"><a href="#三大对象" class="headerlink" title="三大对象"></a>三大对象</h3><h4 id="1-对象类型"><a href="#1-对象类型" class="headerlink" title="1. 对象类型"></a>1. 对象类型</h4><p>Git中的对象主要有三种类型:</p><ul><li><strong>Blob</strong>:表示文件内容。</li><li><strong>Tree</strong>:表示目录,可以包含指向其他blob或tree对象的指针。</li><li><strong>Commit</strong>:表示一次提交,包含指向tree对象的指针、作者信息、提交信息等。</li></ul><h5 id="提交对象(commit)"><a href="#提交对象(commit)" class="headerlink" title="提交对象(commit)"></a><strong>提交对象(commit)</strong></h5><p>提交对象是每次提交时生成的一个对象,记录了:</p><ul><li><strong>提交元数据</strong>:如提交信息、作者、时间戳等。</li><li><strong>父提交</strong>:上一个提交的引用(一个提交可以有多个父提交,用于表示合并)。</li><li><strong>树对象的引用</strong>:提交对象会指向一个根树对象,代表当时项目的整体文件系统快照。<br>提交对象是 Git 的历史记录,通过它可以追踪项目的每个版本、作者、提交时间等信息。</li></ul><h5 id="树对象(tree)"><a href="#树对象(tree)" class="headerlink" title="树对象(tree)"></a><strong>树对象(tree)</strong></h5><p>树对象表示文件系统的目录结构。它:</p><ul><li><strong>包含多个引用</strong>:每个引用要么指向一个文件对象(blob),要么指向另一个子目录的树对象。</li><li><strong>记录文件名和路径</strong>:树对象会保存项目中所有文件和子目录的结构信息,包括文件的名字、类型(文件或目录),以及它们在仓库中的位置。<br>树对象可以嵌套,类似文件系统中的目录结构,一个树对象可以包含其他树对象(子目录),也可以包含文件对象(表示具体的文件内容)。</li></ul><h5 id="文件对象(Blob)"><a href="#文件对象(Blob)" class="headerlink" title="文件对象(Blob)"></a><strong>文件对象(Blob)</strong></h5><p>Blob(Binary Large Object)对象保存了实际的文件内容,Blob 对象本身不包含文件名或路径等元数据。它:</p><ul><li><strong>存储文件内容</strong>:无论是文本文件还是二进制文件,Blob 对象只存储文件的内容本身。</li><li><strong>与文件名、路径无关</strong>:文件名和路径信息都在树对象中管理,因此相同内容的文件在不同的目录或提交中,只需要存储一份 Blob 对象。</li></ul><h5 id="关系总结:如何构成一个提交"><a href="#关系总结:如何构成一个提交" class="headerlink" title="关系总结:如何构成一个提交"></a><strong>关系总结:如何构成一个提交</strong></h5><ul><li><strong>提交对象(commit)</strong> 是顶层对象,指向一个 <strong>树对象(tree)</strong>,代表当时项目的目录结构。</li><li><strong>树对象(tree)</strong> 维护着目录和文件的结构,并通过引用指向文件对象或子目录(另一个树对象)。</li><li><strong>文件对象(Blob)</strong> 存储文件的实际内容,树对象通过引用连接到 Blob 对象。<br>因此,Git 通过提交对象、树对象和Blob对象的层层指向,构成了完整的项目快照。例如,每次提交时:</li></ul><ol><li>Git 会生成一个 <strong>提交对象</strong>,指向项目当前的根 <strong>树对象</strong>。</li><li><strong>树对象</strong> 代表整个项目的目录结构,并指向多个文件对象(Blob)或子目录的树对象。</li><li><strong>Blob 对象</strong> 保存文件内容。<br>这三者相互关联,形成了完整的项目历史和快照。</li></ol><h3 id="图示:对象关系"><a href="#图示:对象关系" class="headerlink" title="图示:对象关系"></a><strong>图示:对象关系</strong></h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">提交对象(<span class="keyword">commit</span>)</span><br><span class="line"> <span class="operator">|</span></span><br><span class="line"> └── 树对象(tree)</span><br><span class="line"> ├── <span class="type">Blob</span> 对象(文件<span class="number">1</span>内容)</span><br><span class="line"> ├── <span class="type">Blob</span> 对象(文件<span class="number">2</span>内容)</span><br><span class="line"> └── 子树对象(子目录)</span><br><span class="line"> ├── <span class="type">Blob</span> 对象(子目录中的文件<span class="number">1</span>内容)</span><br><span class="line"> └── <span class="type">Blob</span> 对象(子目录中的文件<span class="number">2</span>内容)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>通过这种分层结构,Git 能够快速检索项目的历史版本,并且因为Blob对象可以被多个树对象引用,所以相同的文件内容在不同的提交中只需要存储一次,大大提高了存储效率。<br>可以通过 <code>git cat-file -p</code> 命令查看这些对象的关系和详细内容,比如查看提交、树对象和Blob对象的内容:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git cat-file -p <commit-hash></span><br><span class="line">git cat-file -p <tree-hash></span><br><span class="line">git cat-file -p <blob-hash></span><br></pre></td></tr></table></figure><p>可以通过 <code>git cat-file -t</code> 命令查看这些对象的实际类型</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git cat-file -t <commit-hash></span><br><span class="line">git cat-file -t <tree-hash></span><br><span class="line">git cat-file -t <blob-hash></span><br></pre></td></tr></table></figure><h4 id="2-计算哈希值的步骤"><a href="#2-计算哈希值的步骤" class="headerlink" title="2. 计算哈希值的步骤"></a>2. 计算哈希值的步骤</h4><p>在让我们对于每种对象,Git都是如何生成一个唯一的哈希值。计算哈希值的步骤如下:</p><h5 id="Blob(文件内容)"><a href="#Blob(文件内容)" class="headerlink" title="Blob(文件内容)"></a>Blob(文件内容)</h5><ol><li><strong>准备数据</strong>:首先,Git会将文件内容与其类型信息和长度信息组合在一起,形成一个字符串。格式如下:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">blob <size>\0<content></span><br></pre></td></tr></table></figure><ul><li><code><size></code>是文件的字节数。</li><li><code>\0</code>是一个空字符,用于分隔大小和内容。</li><li><code><content></code>是文件的实际内容。</li></ul></li><li><strong>计算哈希</strong>:将上述字符串传递给SHA-1哈希函数,生成一个40字符的十六进制哈希值。</li></ol><h5 id="Tree(目录)"><a href="#Tree(目录)" class="headerlink" title="Tree(目录)"></a>Tree(目录)</h5><ol><li><strong>准备数据</strong>:一个tree对象由多个条目组成,每个条目包含文件模式、文件名和指向blob或子tree对象的SHA-1哈希值。格式如下:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tree <size>\0<entries></span><br></pre></td></tr></table></figure><ul><li><code><size></code>是tree中条目的数量。</li><li><code><entries></code>是所有条目的组合,每个条目都是类似于<code><mode> <filename>\0<sha1></code>的格式。</li></ul></li><li><strong>计算哈希</strong>:同样,将字符串传递给SHA-1哈希函数。</li></ol><h5 id="Commit(提交)"><a href="#Commit(提交)" class="headerlink" title="Commit(提交)"></a>Commit(提交)</h5><ol><li><strong>准备数据</strong>:一个commit对象包含指向tree对象的指针、作者信息、提交时间和提交信息。格式如下:<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">commit <size>\0<tree>\n<parent>\n<author>\n<committer>\n<timestamp>\n<message></span><br></pre></td></tr></table></figure><ul><li><code><size></code>是commit对象的字节数。</li><li><code><tree></code>是指向tree对象的SHA-1。</li><li><code><parent></code>(如果存在)指向父提交的SHA-1。</li><li><code><author></code>和<code><committer></code>包含提交者的信息。</li><li><code><timestamp></code>是提交时间。</li><li><code><message></code>是提交信息。</li></ul></li><li><strong>计算哈希</strong>:将上述字符串传递给SHA-1哈希函数,生成该提交对象的哈希值。</li></ol><h4 id="3-哈希值的存储"><a href="#3-哈希值的存储" class="headerlink" title="3. 哈希值的存储"></a>3. 哈希值的存储</h4><p>Git会将计算出来的哈希值作为对象的唯一标识符,存储在<code>.git/objects</code>目录中。每个对象的内容及其哈希值被保存在文件系统中,以便后续快速检索和验证。</p><h3 id="SHA-1算法"><a href="#SHA-1算法" class="headerlink" title="SHA-1算法"></a>SHA-1算法</h3><p>看了这么多一定对SHA到底什么感到困惑吧,其实没什么高深的,Git就是根据相应对象的具体内容(本质是二进制)算出一个 40 字符的 16 进制字符串,这个字符串就被称为 “散列值” 或 “哈希值”。<br>Git 使用 SHA-1(Secure Hash Algorithm 1)作为其核心机制来唯一标识每个提交、文件对象和目录树。<br>哈希值具有以下特性:</p><ul><li><strong>不可逆性</strong>:从哈希值无法反推原始数据。</li><li><strong>抗碰撞性</strong>:很难找到两个不同的输入产生相同的哈希值。</li><li><strong>小改动大变化</strong>:输入数据的微小变化会导致哈希值的巨大变化。<br>Git利用这个特性实现如下它的几个关键作用:</li></ul><ol><li><p><strong>唯一标识对象</strong>:Git 中的每个文件、每次提交、以及每个目录树的状态都会生成一个 SHA-1 哈希值。Git 通过这个哈希值来区分每个提交,确保同样的文件内容和文件结构不会生成重复的 ID。</p></li><li><p><strong>内容完整性校验</strong>:SHA-1 的<strong>不可逆特性</strong>使得它非常适合<strong>检测文件是否被篡改</strong>。Git 在传输和存储文件时使用 SHA-1 来校验数据的完整性,如果一个文件的内容被改动,它的哈希值也会发生变化,这样可以有效防止数据损坏。</p></li><li><p><strong>分布式优势</strong>:SHA-1 哈希值在 Git 中有一个不可忽视的好处:因为哈希值是根据内容生成的,<strong>不同的开发者对同样的内容会产生相同的哈希值</strong>,这使得 Git 的分布式模型更加高效。</p></li><li><p><strong>不可逆性和冲突</strong>:虽然 SHA-1 理论上会有哈希冲突的可能,但在实际开发中,这种冲突几乎不可能影响到项目的正常运作。SHA-1 的设计保证了它的安全性和性能。</p></li></ol><p>我之前了解过一些区块链的知识,就感觉似曾相识。Git是使用SHA-1而区块链使用的SHA-256,但SHA-256和SHA-1都是加密哈希函数,可以简单的理解为SHA-1是简单版而SHA-256是plus版。因为比特币算是金融领域,需要更高的安全性,区块链算是把hash玩出了花,相较Git有更复杂的数据结构和运算方法。</p><blockquote><p>Git诞生于2005年,是由Linus Torvalds开发的一种分布式版本控制系统,设计之初主要是为了解决Linux内核开发的版本管理需求。它的核心理念是去中心化的分布式存储:每个人可以有一个完整的项目历史副本,且各副本通过哈希值(SHA-1)确保每次变更的唯一性和完整性。<br>区块链则是在2008年中本聪提出的比特币白皮书中被首次介绍的技术,它建立在许多类似Git的去中心化和不可篡改的核心理念上。</p></blockquote><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>这篇文章大致讲解了git中的两个主要模型<strong>快照存储模型</strong>和<strong>不可变对象模型</strong>,略微引出了下哈希,而这两个模型理念的集中应用就是在 <code>.git/objects</code> 目录,下一篇文章会围绕其展开。<br>也是写爽了,发现Git这个坑是越挖越大,越挖越深😓😓😓,或许能出本书呢。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Git通讲-第一章:起源</title>
<link href="/tangBlog/2024/11/02/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%80%E7%AB%A0%EF%BC%9A%E8%B5%B7%E6%BA%90/"/>
<url>/tangBlog/2024/11/02/Git%E9%80%9A%E8%AE%B2-%E7%AC%AC%E4%B8%80%E7%AB%A0%EF%BC%9A%E8%B5%B7%E6%BA%90/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这一篇文章其实我筹谋了好久了,从七月暑假开始之时突然对git的源码感兴趣,想要深入了解一下,也确实看了些资料,但最后还是战线拖太长了,注意力转移到其他处了,写博文的事情就一直在todolist呆到现在🫠。<br>之前也略微写了两篇博文,第一篇大致的记录了git的基础知识包含一些vim的基础知识,第二篇则是主要关注git本地和远程仓库之间的操作,感兴趣的可以爬楼去看。<br>本打算一篇博客就讲完的,但是学了一下,发现可讲的东西确实有些丰富,就打算开一个系列,系统地讲讲Git。这篇文章也是系列的开始之作,希望能有头有尾吧orz。也算给我锻炼下文笔,努力给大家讲得明明白白的。</p><h2 id="引文"><a href="#引文" class="headerlink" title="引文"></a>引文</h2><p>要讲Git是什么,我打算还是先让大家了解下git诞生的目的是什么——<strong>版本控制</strong>🗄️</p><h3 id="版本控制简介"><a href="#版本控制简介" class="headerlink" title="版本控制简介"></a>版本控制简介</h3><p>版本控制系统(Version Control System,简称VCS)是一种软件工具,用于跟踪文件的更改历史,记录代码在不同时间点的状态。它的核心功能是允许开发者保存项目的多个版本,并能够在需要时<strong>回溯、对比、合并</strong>这些版本。通过版本控制,团队中的每个开发者都可以轻松地协同工作,同时确保代码的版本历史清晰可追溯。</p><h3 id="为什么版本控制对软件开发至关重要?"><a href="#为什么版本控制对软件开发至关重要?" class="headerlink" title="为什么版本控制对软件开发至关重要?"></a>为什么版本控制对软件开发至关重要?</h3><ol><li><strong>保护代码历史</strong>:开发者可以在任意时间回滚到之前的某个版本,避免因误操作或错误改动而损坏项目。</li><li><strong>多人协作的基础</strong>:VCS 使得多位开发者可以同时在同一个项目中工作,不必担心互相覆盖或干扰彼此的修改。</li><li><strong>管理和追踪更改</strong>:每次提交代码都会生成一个记录,包括改动内容、时间和贡献者信息,方便审查和调试。</li><li><strong>支持并行开发</strong>:开发者可以在不同的分支上进行并行开发,将不同的功能、修复或实验隔离开,最终可以通过合并整合这些分支。</li></ol><h3 id="传统方式的局限性:手动备份和集中式VCS"><a href="#传统方式的局限性:手动备份和集中式VCS" class="headerlink" title="传统方式的局限性:手动备份和集中式VCS"></a>传统方式的局限性:手动备份和集中式VCS</h3><ul><li><p><strong>手动备份</strong>:在没有版本控制工具的时代,开发者往往通过复制文件夹来保存项目的不同版本。例如,将文件夹命名为 <code>project_v1</code>, <code>project_v2</code> 来手动记录不同的代码版本。然而,这种方式效率低下,容易混淆,难以快速对比不同版本间的差异。而且,随着项目的复杂度增加,手动管理多个副本变得愈发困难。</p></li><li><p><strong>集中式版本控制系统(CVS/Subversion)</strong>:像Subversion (SVN) 这样的早期VCS采取集中式的架构,所有代码和历史记录都存储在中央服务器上,开发者需要实时连接服务器才能访问历史版本。集中式VCS有几个明显的缺点:</p><ol><li><strong>单点故障</strong>:如果服务器发生故障,所有开发者的工作都会停滞,且有可能丢失数据。</li><li><strong>操作效率低</strong>:每次提交、检出或同步都依赖于服务器,操作速度受限于网络延迟。</li><li><strong>协作复杂</strong>:多人同时对同一文件进行修改时,往往容易导致冲突,而集中式系统的分支管理能力较弱,难以灵活处理。</li></ol></li></ul><h2 id="Git的起源故事"><a href="#Git的起源故事" class="headerlink" title="Git的起源故事"></a>Git的起源故事</h2><p>之后我们就来简单的了解一下git的历史起源,当然不想听故事的同学可以直接略过,想当故事听的我推荐下<strong>B站up主北游老土</strong>的相关视频<a href="https://www.bilibili.com/video/BV1uK4y1k79t?vd_source=4cc56f82cd176a294e65e1a6c2943db6">诸神传说:Git的诞生(1)</a>这一系列讲Git和Github的发展历史的视频,我觉得还是蛮有意思的。最让我震惊的还是其中讲到的微软收购github的行为吧,让我对闭源和开源之间的关系有了新的认知,印象中的闭源地头蛇微软居然还维持这开源世界的扛把子github。</p><h4 id="背景故事"><a href="#背景故事" class="headerlink" title="背景故事"></a>背景故事</h4><p>在2005年,Linux内核项目的开发团队面临着一个巨大的挑战。作为开源社区中最活跃和复杂的项目之一,Linux内核项目依赖于一个名为 <strong>[[BitKeeper]]</strong> 的版本控制系统(VCS)。然而,BitKeeper并非开源工具,它是由商业公司提供的,并免费提供给Linux开发者使用。Andrew Tridgell通过逆向工程试图破解其协议,以便在不违反许可条款的情况下使用BitKeeper。这引起了BitKeeper公司(由Larry McVoy领导)的不满,导致他们决定撤回向开源社区免费提供BitKeeper的许可。这一事件直接推动了 <strong>Linus Torvalds</strong> 创建Git。<br>(<strong>Andrew Tridgell</strong>,他也是一位非常著名的开源软件开发者。他最为人知的成就是开发[[Samba]],一个开源的软件套件,能够在不同的操作系统之间实现文件和打印共享。)<br>Linus,作为Linux内核的创始人和主要维护者,意识到Linux内核开发迫切需要一种高效、可靠且适合开源社区的版本控制工具。而当时市场上现有的版本控制工具,比如CVS和Subversion(SVN),都无法满足大规模、快速变化的开源项目的需求。出于对这些工具的性能不满,Linus决定自己开发一款新的工具—— <strong>Git</strong>。<br><strong>2005年4月3日</strong>:Linus Torvalds正式开始开发 Git。这是在 BitKeeper 停止免费向开源项目提供支持之后,他决定创建一个新的分布式版本控制系统的起点。<strong>2005年4月7日</strong>:仅仅四天后,Linus 发布了 Git 的第一个可用版本(0.1)。这个初始版本包含了 Git 的核心功能,如基本的数据结构和操作。 <strong>接下来的几周内</strong>: Linus 和其他早期贡献者对 Git 进行了快速迭代和改进,逐步完善了其稳定性和功能,以确保它能够胜任管理 Linux 内核代码库的任务。<br>虽然 Git 的初始版本在四天内发布,但要达到真正用于管理 Linux 内核项目的成熟程度,大约花费了一个月的时间。这包括了持续的优化、功能添加以及社区的反馈和贡献,确保 Git 能够高效、稳定地支持大型项目的需求。<br>Git之所以能够迅速获得广泛认可,也正是因为它在Linux内核项目这种超大规模、分布式协作项目中展现了极强的性能和稳定性。这种环境对版本控制系统提出了极高的要求,例如高并发、多分支的并行开发以及快速的代码合并,而Git的设计正是为了满足这些需求。因此,Git在开发Linux内核时的表现,验证了它在处理大型代码库时的稳定性和高效性,证明了它的性能下限。</p><h2 id="Git的设计原则"><a href="#Git的设计原则" class="headerlink" title="Git的设计原则"></a>Git的设计原则</h2><p>一款好的工具应该设计目的明确,针对特定的问题和痛点需求进行解决,否则只会将精力白白浪费在开发出一些无关痛痒、毫无关联乃至是一些画蛇添足的功能上。</p><h4 id="开源社区的需求"><a href="#开源社区的需求" class="headerlink" title="开源社区的需求"></a>开源社区的需求</h4><p>Git 之所以诞生,不仅仅是为了替代BitKeeper,更是为了解决Linux开发过程中面对的两个核心问题:</p><ol><li><strong>分布式开发</strong>:开源项目往往分散在全球各地,多个开发者同时在不同的时间段、不同的地点贡献代码,因此需要一个能支持 <strong>分布式开发</strong> 的版本控制工具。</li><li><strong>非线性开发流程</strong>:在内核开发中,不同的功能、bug修复、优化等需要同时进行,版本控制工具必须具备强大的 <strong>分支与合并</strong> 能力,以便开发者能在不同分支上并行工作,并最终将这些工作无缝合并到主线代码中。</li></ol><h4 id="Linus对现有工具的不满"><a href="#Linus对现有工具的不满" class="headerlink" title="Linus对现有工具的不满"></a>Linus对现有工具的不满</h4><p>Linus 对现有集中式版本控制工具(如CVS、Subversion)的不满主要集中在几个方面:</p><ol><li><strong>性能问题</strong>:现有工具在处理大型代码库时效率较低,提交、合并操作往往非常缓慢,不能满足Linux内核项目的需求。</li><li><strong>分支管理不灵活</strong>:传统工具的分支管理模型复杂且笨重,创建或合并分支的成本高昂。对于频繁需要分支和合并的开源项目,无法做到灵活处理。</li><li><strong>中央服务器的依赖</strong>:这些工具依赖中央服务器,这意味着每一次提交、拉取都需要与服务器交互,降低了开发效率。而且,中央服务器一旦故障,整个开发流程可能会瘫痪。<br>在这些不满的推动下,Linus决定从根本上重构版本控制工具的架构,创造一种全新的分布式系统,这就是Git。</li></ol><h4 id="设计原则"><a href="#设计原则" class="headerlink" title="设计原则"></a>设计原则</h4><p>Linus在设计Git时,确立了几个核心目标,这些目标奠定了Git的基础:</p><ol><li><p><strong>快速</strong>:Git的每个操作(如提交、合并、分支切换等)都需要非常高效,能够快速处理数以千计的文件和海量代码。Linus的目标是让Git的基本操作比现有工具快一个数量级。</p></li><li><p><strong>高效处理大型代码库</strong>:Linux内核是一个庞大的项目,Git需要能够轻松管理数百万行代码,同时保证资源占用少,操作流畅。</p></li><li><p><strong>支持非线性开发</strong>:Git必须能够处理复杂的并行开发工作流,支持开发者自由创建、切换和合并分支。Git的分支和合并操作设计得极为轻量,让开发者可以毫无顾虑地频繁创建和合并分支。</p></li><li><p><strong>数据完整性与安全性</strong>:Git的每一次提交都会通过SHA-1哈希生成唯一的ID,确保每个提交的内容不可篡改,同时保障代码历史记录的完整性。</p></li></ol><h2 id="Git核心理念"><a href="#Git核心理念" class="headerlink" title="Git核心理念"></a>Git核心理念</h2><p>之后就是引入我们这系列的重头戏了,Git的设计理念围绕着分布式控制、快照管理、不可变对象模型和灵活的分支策略关键概念展开,我会在接下的文章逐个讲解这些核心理念的实现原理。</p><h4 id="1-分布式版本控制"><a href="#1-分布式版本控制" class="headerlink" title="1. 分布式版本控制"></a>1. 分布式版本控制</h4><p>与传统的集中式版本控制系统(如Subversion)不同,Git是一个分布式版本控制系统。这意味着每个开发者在自己的机器上都有一个完整的代码库和历史记录副本,而不依赖于中央服务器。这种设计带来了几个重要优势:</p><ul><li><strong>本地操作</strong>:开发者可以在没有网络连接的情况下进行提交、查看历史和创建分支等操作,极大提高了操作的灵活性和效率。</li><li><strong>数据冗余</strong>:即使中央服务器出现故障,开发者依然可以从本地仓库中获取完整的项目历史和版本,这降低了数据丢失的风险。</li><li><strong>协作效率</strong>:多个开发者可以同时在不同的本地仓库上进行工作,最后将更改合并到主仓库,避免了集中式系统中可能出现的竞争与冲突。</li></ul><h4 id="2-快照与差异"><a href="#2-快照与差异" class="headerlink" title="2. 快照与差异"></a>2. 快照与差异</h4><p>Git采用的核心理念之一是通过 <strong>快照</strong> 来记录文件的状态,而不是简单记录文件的差异(diff)。在每次提交时,Git会保存当前工作区的快照,记录项目的完整状态。这一机制带来了以下优势:</p><ul><li><strong>高效存储</strong>:虽然Git保存的是每次提交的快照,但它只会存储自上次快照以来发生变化的部分。这意味着即使是大型项目,Git也能有效管理存储空间。</li><li><strong>版本回溯</strong>:由于每次提交都是一个完整的快照,开发者可以轻松地回溯到任何历史版本,无需担心文件之间的复杂差异。<br>Git使用的 <code>.git</code> 文件夹是版本控制的核心,里面包含了所有的版本历史、对象和元数据。开发者在项目根目录下的 <code>.git</code> 文件夹中,可以找到有关每个提交的详细信息,包括树状结构、作者、时间戳等。这使得Git能够快速检索和还原任何历史版本。</li></ul><h4 id="3-不可变对象模型"><a href="#3-不可变对象模型" class="headerlink" title="3. 不可变对象模型"></a>3. 不可变对象模型</h4><p>Git的内部数据结构依赖于 <strong>加密哈希函数(SHA-1)</strong>,为每个对象(如文件和提交)生成唯一标识。这种设计确保了数据的不可变性:</p><ul><li><strong>数据完整性</strong>:每个对象的内容会被哈希处理,任何对内容的修改都会导致哈希值的变化,从而提示开发者数据已被更改。</li><li><strong>一致性</strong>:由于对象的内容与其哈希值紧密关联,Git能够保证版本库的一致性,防止数据丢失或损坏。这种机制使得任何用户都可以安全地信任本地的Git仓库。</li></ul><h4 id="4-分支模型"><a href="#4-分支模型" class="headerlink" title="4.分支模型"></a>4.分支模型</h4><p>Git的分支模型是其另一大核心优势,采用 <strong>轻量级分支</strong> 的设计,允许开发者快速、灵活地创建和管理分支:</p><ul><li><strong>快速创建与切换</strong>:在Git中,创建一个新分支几乎是瞬时的,因为它仅仅是一个指向提交的指针。开发者可以毫不犹豫地创建多个分支,用于开发不同功能、修复bug或进行实验。</li><li><strong>无成本的分支管理</strong>:由于创建和切换分支的成本极低,Git鼓励开发者频繁使用分支。这使得团队能够在开发过程中保持清晰的结构,轻松地进行并行开发。</li><li><strong>便捷的合并操作</strong>:Git提供了强大的合并工具,使得将不同分支的工作合并变得简单。开发者可以轻松地将功能分支合并到主分支,或者处理合并冲突。</li></ul><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>这篇博文主要还是想从Git诞生的故事开始,简单探讨一些Git的设计原则和核心理念,或许会有些文绉绉的,但是我想任何一个伟大的事情诞生都不是凭空冒出来的,都是背负着过往的历史,立足当下的现实,努力去开创未来的前行方向的。Git的成功不是人为的偶然,而是历史的必然。</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>Obsidian个人知识管理工具体验分享</title>
<link href="/tangBlog/2024/10/24/Obsidian%E4%B8%AA%E4%BA%BA%E7%9F%A5%E8%AF%86%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7%E4%BD%93%E9%AA%8C%E5%88%86%E4%BA%AB/"/>
<url>/tangBlog/2024/10/24/Obsidian%E4%B8%AA%E4%BA%BA%E7%9F%A5%E8%AF%86%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7%E4%BD%93%E9%AA%8C%E5%88%86%E4%BA%AB/</url>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>之前都是在用Typora作为我的文字记录工具,但暑期的时候看到RSS突发奇想,想要搞一套完整的<strong>信息流</strong>,于是就在寻找合适我的笔记软件,最后选择了Obsidian。<br>obsidian自从9月初使用以来了已将近两个月了,当初忘记写初体验了,现在补上一篇充分体验后的产品报告。</p><h1 id="竞品比较"><a href="#竞品比较" class="headerlink" title="竞品比较"></a>竞品比较</h1><p>当初本就抱着all in one的目的,想找一个功能全面的笔记软件,能满足我的“跨平台记录阅读”的需求,当时比较了市面上主流的几款笔记软件——Notion、Obsidian、印象、Flomo。当然还有幕布、、Bear、石墨、Logseq,但这些都因为精力有限,各种原因导致没能在一开始吸引我进行更深入的体验。</p><h3 id="印象笔记"><a href="#印象笔记" class="headerlink" title="印象笔记"></a>印象笔记</h3><p>印象笔记就纯纯炮灰🤦♂️,弹广告我就接受不了一点,各种功能都要付费,更其他俩个一比就是垃圾,特别是国产收购后就开始日渐臃肿发福。还是那句话“我本可以忍受黑暗,如果我不曾见过光明”。</p><h3 id="Flomo"><a href="#Flomo" class="headerlink" title="Flomo"></a>Flomo</h3><p>Flomo的设计的核心理念是“卡牌笔记”,适合碎片化、片段性的文字记录,经过轻度体验不切我本人需求,但是其产品概念还是不错的,很有发展前途,或许将来我会使用。个人感觉flomo和anki联动一下或许能有更好的体验。</p><h3 id="Notion"><a href="#Notion" class="headerlink" title="Notion"></a>Notion</h3><p>确实是个好工具,适合捣鼓的人,有无限可能的人,我愿称之为<strong>最强赛博手账</strong></p><ul><li><strong>优点</strong><ul><li><strong>多功能性</strong>:Notion算是一个很有个性的笔记软件,将<strong>数据库</strong>的概念融入的笔记中,带来了无限的可能。</li><li><strong>高颜值</strong>:Notion的<strong>审美</strong>确实一绝,简单一搞就相当有质感,使用Notion的时候有种做手账的感觉。</li><li><strong>模板</strong>:提供丰富的模板库,用户可以轻松创建不同类型的页面。</li><li><strong>协作功能</strong>:支持多人实时协作,适合团队使用,允许评论、提及等互动。</li></ul></li><li><strong>缺点</strong><ul><li><strong>复杂性</strong>:若需求只是想记录一下文字而言,我认为其功能<strong>过于丰富</strong>,我怕我一搞起来就上头了,不在关注文字本身而是各种形式。其的上手也有较为陡峭的学习曲线。</li><li><strong>通用性</strong>:此外也因为notion形式上的创新,导致了其不像别的软件那样可以轻易的导出md。</li><li><strong>云存储</strong>:数据存储在云端,方便访问,但需要关注隐私和数据安全。考虑到数据安全,我还是有些忌惮。</li></ul></li></ul><h3 id="Obsidian"><a href="#Obsidian" class="headerlink" title="Obsidian"></a>Obsidian</h3><p><strong>地表最强个人知识管理工具</strong></p><ul><li>优点<ul><li><strong>双链</strong>:最吸引我的就是其笔记中的双链功能,当然使用双链是现代笔记的一个趋势并非obsidian特有,但是其把双链的位置摆在了较高的优先级,围绕其开发了许多有特点的功能,像是graph view,反向链接</li><li><strong>插件系统</strong>:Obsidian 拥有丰富的插件生态,官方和社区开发的插件极大扩展了其功能。用户可以根据需求选择不同插件,几乎可以无限制地自定义 Obsidian 的功能。通过插件按需安装的特点就可以让ob变成你喜欢的样子,完美贴合你的需求。可以通过插件实现思维导图,md转word······</li><li><strong>主题和样式自定义</strong>:Obsidian 支持用户使用 CSS 自定义界面样式,此外也有丰富的社区主题供用户选择,帮助打造个性化的笔记体验。</li><li><strong>跨设备同步</strong>:Obsidian 默认将笔记存储在本地,但它支持通过多种方式实现跨设备同步,我就通过使用Remotely插件白嫖坚果云,实现云同步。此外obsidian适配安卓、ios、windows、mac,通过这办法就可以在每个终端上记录并同步笔记,发挥移动端和桌面端不同使用场景的优势。我认为,移动端码长篇文章过去痛苦,但是却可以满足随记的需求,将瞬时的思想抓住。</li></ul></li><li>缺点<ul><li><strong>没有在线协作</strong>:Obsidian 的设计主要面向个人使用,缺乏实时在线协作的功能。如果需要多人同时编辑文档,Notion 等其他工具可能更合适。</li><li><strong>界面简约但不够直观</strong>:虽然 Obsidian 的界面简洁,但对于一些用户来说,可能会感到缺乏视觉上的吸引力。此外,部分功能的访问方式(如命令面板)可能不够直观。和notion比较起来还是能感受都审美上的差距。</li></ul></li></ul><p>notion和obsidian就像在两个不同的方向生长,notion是向内生长,通过自身的架构,带来功能的丰富性;而obsidian则是向外生长,靠插件来拓张功能,来满足不同需求。</p><h1 id="个人使用体验"><a href="#个人使用体验" class="headerlink" title="个人使用体验"></a>个人使用体验</h1><p>我现在基本上将所有要码字的笔记都转移到了ob上去了,包括撰写个人博客,记录平时网络上看到的优秀观点、灵感一现的想法等等。</p><h1 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h1><p>此外不得不提到是关于前言的callback,最近在github上发现了基于RSS开发的<strong>Follow</strong>,试图将所有<strong>信息源all in one</strong>,同时follow支持obsidian保存。无敌了😍,这真的无敌了😍,我暑假设想的信息流齐全了,同时是满足了信息的收集、呈现、记录和输出。Follow我也会持续关注其发展,有机会的也可以写下分享。</p>]]></content>
<tags>
<tag> 科技分享 </tag>
</tags>
</entry>
<entry>
<title>Tailwind CSS初体验</title>
<link href="/tangBlog/2024/10/10/Tailwind-CSS%E5%88%9D%E4%BD%93%E9%AA%8C/"/>
<url>/tangBlog/2024/10/10/Tailwind-CSS%E5%88%9D%E4%BD%93%E9%AA%8C/</url>
<content type="html"><![CDATA[<p>还是把我想说的话放前面吧,对于独立React小项目的快速开发来说,Tailwind是值得尝试的,我也就仅仅讨论在我目前的状态下(苦逼大学生🤓,个人独立项目(没前端队友,没专业设计)),被拉去打比赛,最后还是“能者多劳”,前端的活最终还是一个人大包大揽🚬🚬🚬。</p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>好久没写博文了,最近在努力从安卓转前端,一直都在学(技术力不够不配写博文)。看了<a href="https://www.robinwieruch.de/react-css-styling/">如何在 React 中使用 CSS 样式 — How to CSS Style in React (robinwieruch.de)</a>这篇文章,了解了下Tailwind Css,被其的高效惊喜到了。</p><p>我个人的感觉Tailwind是在传统的css上抽象了一层,一切优缺点都是有此带来的。不像是Sass,只是作为css的一个拓展超集,爱用就用,不用不用。</p><h2 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h2><h3 id="团队配合"><a href="#团队配合" class="headerlink" title="团队配合"></a>团队配合</h3><p>本来奇怪为什么没看到什么主流项目在用,搜索了下主要争议点还是在-目前不适合团队配合,毕竟原子化CSS是学习成本的东西也没流行到成为一种规范,在需要多人配合中大型项目不好合作。目前这东西还得是公司自上而下的推行才有可能在团队项目里使用。<br>这一点是不得不去考虑的,毕竟偏离主流的技术在大型项目中是没有用武之地的,这东西在开源社区中也很难融入,仍然得写回Sass。</p><h3 id="复杂性转移"><a href="#复杂性转移" class="headerlink" title="复杂性转移"></a>复杂性转移</h3><p>这其实我感觉也不能算缺点。<br>虽然有些人也有提到tw在实现某些业务方面(不同主题切换)会比较繁琐,但我认为是属于次要的,毕竟其本质任务还是负责view的部分。tw将某些常用的任务简化后不可避免的将复杂性转移到其他地方,只要不是致命的缺陷(无法通过技术解决),将常用的功能简化,不常用的功能复杂化也是可以接受的,平均下来仍然是好事。</p><h2 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h2><h3 id="写起来爽"><a href="#写起来爽" class="headerlink" title="写起来爽"></a>写起来爽</h3><p>我一个独狗🚬🤓写起来怎么爽怎么来(bushi),官网介绍的就挺准确的“只需书写 HTML 代码,无需书写 CSS,即可快速构建美观的网站”,主要还是得接受这种unocss思想,我现在还说不准其对错,感受就完事了。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>究竟tw这类Utility-First-CSS能不能发展完善,将来有什么发展的方向还是值得瞻望的</p>]]></content>
<tags>
<tag> 科技分享 </tag>
</tags>
</entry>
<entry>
<title>C盘扩容</title>
<link href="/tangBlog/2024/08/24/C%E7%9B%98%E6%89%A9%E5%AE%B9/"/>
<url>/tangBlog/2024/08/24/C%E7%9B%98%E6%89%A9%E5%AE%B9/</url>
<content type="html"><![CDATA[<h1 id="C盘扩容"><a href="#C盘扩容" class="headerlink" title="C盘扩容"></a>C盘扩容</h1><blockquote><p>核心理念就是将与C盘相邻近的磁盘彻底清空, 然后通过磁盘管理的拓展卷现实C盘扩容.</p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>鄙人不才,分盘的时候C盘分的不够大,导致C盘一直处于爆红状态,遂借加装硬盘(当然如果内存够用可以不加)之机给C盘扩容一下。没想到遇到不少问题,于是有了这篇文章。希望能给有相关需要的xdm一定的帮助。</p><h2 id="具体操作"><a href="#具体操作" class="headerlink" title="具体操作"></a>具体操作</h2><h3 id="1-复制文件"><a href="#1-复制文件" class="headerlink" title="1.复制文件"></a>1.复制文件</h3><ul><li><p><strong>打开磁盘管理器</strong>:右键任务栏的🪟windows图标</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/1.jpg" alt="1"></p></li><li><p>这是我改后的磁盘分区,如果你的C盘右边紧挨的磁盘(之后文章里默认是D盘)里没安装什么流氓软件(360,百度网盘之类的),那你很幸运,很简单就能把目标完成了。</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/2.jpg" alt="2"></p><p>如果很不幸你像我一样安装了<strong>百度网盘,360zip,迅雷</strong>之类的会在后台偷偷运行的软件,在后面的删除文件环节你就得好好看了🤷♂️</p></li><li><p><strong>分盘</strong>:右键D盘(我这里因为已经改好了,所有是G盘),点击<strong>压缩卷</strong>,分出足够空间,然后<strong>新加卷</strong></p></li><li><p><strong>转移资源</strong>:将D盘的资源全部复制到<strong>新加卷</strong>中</p></li></ul><h3 id="2-清空D盘"><a href="#2-清空D盘" class="headerlink" title="2.清空D盘"></a>2.清空D盘</h3><blockquote><p>这是本篇文章的初始写作的目的,有些文件是真的逆天,无法正常彻底删除</p></blockquote><p>接下来下内容根据本人的亲身体会,大致麻烦程度逐渐递增,致敬传奇耐删王🖖</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/3.jpg" alt="3"></p><h4 id="I-普通文件"><a href="#I-普通文件" class="headerlink" title="I.普通文件"></a>I.普通文件</h4><p>直接删就完事了,一股脑扔垃圾桶里</p><h4 id="II-NUL"><a href="#II-NUL" class="headerlink" title="II.NUL"></a>II.NUL</h4><p>应该还有别的删除方法,我是用git bash</p><ul><li><p><strong>使用Git Bash</strong>: 右键,使用git bash 。有git应该知道怎么用,没git的话可以下一个,或者网上在查查别的办法</p></li><li><p><strong>使用命令行</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim NUL</span><br></pre></td></tr></table></figure></li><li><p><strong>退出</strong>什么都不用干,输入<code>:wq</code>退出就行了</p></li></ul><p>就奇迹般✨的消失了,具体原因我也不知道</p><h4 id="III-运行程序"><a href="#III-运行程序" class="headerlink" title="III.运行程序"></a>III.运行程序</h4><p>有些文件由于还在后台运行,无法直接删除,需要先杀进程</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/5.jpg" alt="5"></p><ul><li><strong>打开任务管理器</strong>:Ctrl+Shift+Esc</li></ul><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/8.jpg" alt="8"></p><ul><li><strong>结束任务</strong>:<ul><li>有些文件删除时会提示什么XX进程还在运行无法删除,根据其给的提示,在任务管理器中找到相应进程,右键点击结束进程。</li><li>有些流氓软件会隐藏具体名称,那就得搜索一下对症下药了</li></ul></li></ul><p>结束进程占用后一般软件就可以删除了</p><h4 id="IV-删注册表"><a href="#IV-删注册表" class="headerlink" title="IV.删注册表"></a>IV.删注册表</h4><p>像是360和百度网盘这俩个病毒就得更进一步,除了杀进程外还要在注册表里删除</p><ul><li><p><strong>打开注册表</strong>:Win+R 输入regedit</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/6.jpg" alt="6"></p></li><li><p><strong>删除:</strong> 在<code>计算机\HKEY_CURRENT_USER\Software\</code>路径下,看到baidu删就完事了,看到360删就完事了</p></li></ul><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/7.jpg" alt="7"></p><p>如此再试试,就会发现360zip可以删除了,但是百度网盘还是不可以删除,恭喜<strong>百度网盘</strong>🎉🎉🎉,成为活到最后的传奇耐删王</p><h4 id="V-删除百度网盘"><a href="#V-删除百度网盘" class="headerlink" title="V.删除百度网盘"></a>V.删除百度网盘</h4><p>不得不是百度网盘是真dog,打开文件一看吼,还有个<code>YunShellExtV164.dll</code>文件还活着,好家伙在<strong>资源管理器</strong>打开,这种通过正常重启是没用的。</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/9.jpg" alt="9"></p><ul><li><p><strong>记住文件位置</strong>:把这个.dll文件的位置记住,因为接下来你就不能用资源管理器了</p></li><li><p><strong>打开任务管理器</strong>:Ctrl+Shift+Esc</p></li><li><p><strong>关闭资源管理器</strong>:搜索explorer.exe文件,右键结束进程</p><p>oops!放轻松黑屏是正常的,会回来的</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/10.jpg" alt="10"></p></li><li><p><strong>运行新任务:</strong> 右上角有个运行新任务,点他,然后输入<code>cmd</code>打开命令行</p></li><li><p><strong>输入命令</strong>: 如图,根据之前记住的绝对路径,把它删掉</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/11.jpg" alt="11"></p></li><li><p><strong>重启资源管理器</strong>:点击运行新任务,输入explorer.exe,成功打赢复活赛了</p></li></ul><p>打开百度网盘的文件再看看,没辣!</p><h3 id="3-删除虚拟内存"><a href="#3-删除虚拟内存" class="headerlink" title="3.删除虚拟内存"></a>3.删除虚拟内存</h3><p>此时D盘应该彻底清空了,里面什么文件都没有了,但有可能还会碰到一个问题</p><p>重新打开磁盘管理器,对D:仍然无法使用<strong>删除卷</strong>,打开资源管理器一看,发现D:还是有些内存,这就可能是由于<strong>虚拟内存</strong>的原因了</p><p>没问题直接第4步</p><ul><li><strong>打开高级系统设置:</strong>右下角🪟搜索高级系统设置</li></ul><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/12.jpg" alt="12"></p><p>在 性能 里点击 设置</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/13.jpg" alt="13"></p><p>点击更改</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/14.jpg" alt="14"></p><p>选择D:盘,再点击<strong>无分页文件</strong>,最后点击设置</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/15.jpg" alt="15"></p><h3 id="4-删除卷-扩展卷"><a href="#4-删除卷-扩展卷" class="headerlink" title="4.删除卷+扩展卷"></a>4.删除卷+扩展卷</h3><ul><li><p><strong>打开磁盘管理器</strong>:右键任务栏的🪟windows图标,点击磁盘管理</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/1.jpg" alt="1"></p></li><li><p><strong>删除卷</strong>:右键D盘,点击删除卷</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/2.jpg" alt="2"></p></li><li><p><strong>拓展卷</strong>:右键C盘,点击<strong>拓展卷</strong>,分配你觉得合适的大小</p></li></ul><p>ok了,这大致就好了,C盘也打赢复活赛了</p><h3 id="5-重新分配磁盘路径"><a href="#5-重新分配磁盘路径" class="headerlink" title="5.重新分配磁盘路径"></a>5.重新分配磁盘路径</h3><p>有些教程说把原来从D:导出的资源重新再导回D:去,可以是可以这样,但不如直接改磁盘路径👍</p><ul><li><p><strong>打开磁盘管理</strong></p></li><li><p><strong>重新分配磁盘驱动号</strong>:右键之前D盘转移文件的磁盘,点击重新分配磁盘驱动号</p></li><li><p><strong>更改为D盘</strong>:点击更改,将字符选择为D</p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/16.jpg" alt="16"></p><p>这样之前文件配置好的依赖路径就不会改变了,原先装在D盘的程序也可以正常运行了</p></li></ul><hr><p>我这也遇到了一些麻烦,不让我这么操作,于是乎又得改注册表了/(ㄒoㄒ)/~~</p><ul><li><p><strong>打开注册表</strong>:Win+R 输入<strong>regedit</strong></p><p><img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/6.jpg" alt="6"></p></li><li><p><strong>更改:</strong> 在<code>计算机\HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices</code>路径下,更改相应的文件名,我这里是把G改成D<img src="/tangBlog/../images/C%E7%9B%98%E6%89%A9%E5%AE%B9/17.jpg" alt="17"></p></li><li><p><strong>重启电脑</strong></p></li></ul><p>这是真没问题了</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>第一次写这种教程真累ouo,觉得好就给个赞吧,有问题可以评论(我看到如果会的话会回)</p>]]></content>
<tags>
<tag> 计算机系统 </tag>
</tags>
</entry>
<entry>
<title>【计算机科学概论】程序设计层</title>
<link href="/tangBlog/2024/08/10/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E6%A6%82%E8%AE%BA%E3%80%91%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E5%B1%82/"/>
<url>/tangBlog/2024/08/10/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E6%A6%82%E8%AE%BA%E3%80%91%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E5%B1%82/</url>
<content type="html"><![CDATA[<h1 id="程序设计层"><a href="#程序设计层" class="headerlink" title="程序设计层"></a>程序设计层</h1><p>程序设计层讨论的是如何使用计算机系统,引出机器语言</p><ul><li><strong>低级程序设计语言</strong><ul><li><strong>计算机操作</strong><ul><li><strong>可编程&存储&检索&处理</strong></li></ul></li><li><strong>[[机器语言(Machine Language)]]</strong>:由计算机直接使用的二进制编码指令构成的语言<ul><li><strong>[[硬件依赖性]]</strong>: 机器代码因机器的不同而不同,即每种类型的CPU都有它能理解的机器语言</li><li><strong>[[解决机器语言不同的方法]]</strong><ul><li><strong>[[虚拟机(Virtual Machine,VM)]]</strong><ul><li><strong>[[JVM]]</strong></li><li><strong>Pep/8</strong>–[[计算机科学概论原书第5版.pdf#page=103|计算机基础科学概论Pep/8]]<ul><li><strong>体系结构</strong>![[Pasted image 20240808175802.png]]</li><li><strong>指令格式</strong>![[Pasted image 20240808180717.png]]<ul><li><strong>8位指令说明符(可选的)</strong>:说明要执行上面操作和如何解释操作数的位置.指令说明符的格式根据表示一个具体操作所用的比特数不同而不同![[Pasted image 20240808180741.png]]<ul><li><strong>操作码(4bit-8bit)</strong>:长度在4bit到8bit不等<ul><li><strong>4bit操作码</strong><ul><li>第5位为<strong>寄存器说明符</strong>:指定使用哪个寄存器</li><li>3bit<strong>寻址模式说明符</strong>:表示怎么解析指令中的操作数部分👇<ul><li><strong>寻址模式</strong><ul><li><strong>立即寻址模式</strong>![[Pasted image 20240808181718.png]]</li><li><strong>直接寻址模式</strong>![[Pasted image 20240808181731.png]]</li></ul></li></ul></li></ul></li></ul></li></ul></li><li><strong>16位操作数说明符</strong>:操作数的本身或者操作数的地址(有些指令没有操作数说明符)</li><li><strong>一元指令</strong>:没有操作数(要处理的数据)</li></ul></li></ul></li></ul></li></ul></li></ul></li><li><strong>[[汇编语言(Assembly Language)]]</strong>:一种低级语言,用助记码表示特定计算机的机器语言指令<ul><li>汇编器(assembler):把汇编语言程序翻译成机器码的程序![[Pasted image 20240808221123.png]]</li><li><strong>汇编器指令(assembler directive)</strong>:翻译程序使用的指令</li></ul></li><li><strong>伪代码(peseudocode)</strong>:一种表达算法的语言</li></ul></li><li><strong>算法与数据结构</strong><ul><li><strong>[[算法(algorithm)]]</strong>:在有限的时间内用有效的数据解决问题或子问题的明确指令集合</li><li><strong>[[数据结构(data structure)]]</strong>:一种抽象数据类型中的符合数据域的实现</li></ul></li><li><strong>面向对象设计与高级程序语言设计</strong><ul><li><strong>面向对象设计OOD</strong></li><li><strong>高级程序语言</strong><ul><li><strong>翻译过程</strong><ul><li><strong>编译器(compiler)</strong>:把高级语言编写的程序翻译成机器码的程序![[Pasted image 20240808223347.png]]<ul><li>同样具有硬件依赖性,多类型机器上使用一个高级语言,需有多个编译器</li></ul></li><li><strong>解释器(interpreter)</strong>:输入用高级语言编写的程序,指导计算机执行每个语言指定的动作的程序(边翻译边执行)</li><li><strong>[[JVM]]</strong><ul><li><strong>[[java语法回眸|java]]</strong></li><li><strong>字节码(bytecode)</strong>:编译Java源代码使用的标准机器语言</li></ul></li></ul></li><li><strong>[[程序设计语言范型]]</strong><ul><li><strong>面向过程编程(Procedural Programming)</strong></li><li><strong>面向对象编程(Object-Oriented Programming,OOP)</strong></li></ul></li></ul></li></ul></li></ul>]]></content>
<tags>
<tag> 读书笔记 </tag>
</tags>
</entry>
<entry>
<title>【计算机科学概论】操作系统层</title>
<link href="/tangBlog/2024/08/09/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E6%A6%82%E8%AE%BA%E3%80%91%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%B1%82/"/>
<url>/tangBlog/2024/08/09/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E6%A6%82%E8%AE%BA%E3%80%91%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%B1%82/</url>
<content type="html"><![CDATA[<h1 id="操作系统层"><a href="#操作系统层" class="headerlink" title="操作系统层"></a>操作系统层</h1><p>操作系统层负责将硬件和软件结合起来,负责计算机资源的分配</p><ul><li><strong>操作系统(operating system,OS)</strong>:管理计算机资源并为系统交互提供界面的系统软件![[Pasted image 20240809203501.png]]<ul><li><strong>软件分类</strong><ul><li><strong>应用软件(application software):</strong> 帮助我们解决现实世界问题的程序</li><li><strong>系统软件(system software):</strong> 管理计算机系统并与硬件进行交互的程序</li></ul></li><li><strong>多道程序设计(multiprogramming):</strong> 同时在主存中驻留多个程序,由它们竞争CPU的技术</li><li><strong>[[操作系统发展史]]</strong><ul><li><strong>[[批处理]]</strong></li><li><strong>[[分时]]</strong><ul><li><strong>虚拟机(virtual machine)</strong></li></ul></li></ul></li><li><strong>[[内存管理(memory management)]]</strong>–[[计算机科学概论原书第5版.pdf#page=235|计算机科学概论10.2内存管理]]<ul><li><strong>主存</strong>:所有程序在执行时都存储在主存中.这些程序的数据也都存储在主存中,以便程序能够访问它们<ul><li><strong>逻辑地址(logical address)</strong>:对一个存储值的引用,是相对于引用它的程序的</li><li><strong>物理地址(physical address)</strong>: 主存设备中的真实地址</li><li><strong>地址联编(address binding</strong>):逻辑地址和物理地址间的映射</li></ul></li><li><strong>单块内存管理(single contiguous memory management):</strong> 把应用程序载入一段连续的内存区域的内存管理方法![[Pasted image 20240809224200.png]]</li><li><strong>分区内存管理</strong>![[Pasted image 20240809224601.png]]<ul><li><strong>固定分区法(fixed-partition technique):</strong> 把内存分成特定数目的分区以载入程序的内存管理方法<ul><li><strong>适合最先匹配和最佳匹配</strong></li></ul></li><li><strong>动态分区法(dynamic-partition technique):</strong> 根据容纳程序的需要对内存分区的内存管理方法<ul><li><strong>适合最差匹配</strong></li></ul></li><li><strong>基址寄存器(base register):</strong> 存放当前分区的起始地址的寄存器</li><li><strong>界限寄存器(bounds register):</strong> 存放当前分区的长度的寄存器</li><li><strong>分区选择法</strong><ul><li><strong>最先匹配</strong>:把第一个足够容纳程序的分区分配给程序</li><li><strong>最佳匹配</strong>:把最小的能够容纳程序的分区分配给它</li><li><strong>最差匹配</strong>:把最大的能够容纳程序的分区分配给它</li><li>![[Pasted image 20240809225318.png]]</li></ul></li></ul></li><li><strong>✨分页内存管理(paged memory technique):</strong> 主存被分成小的大小固定的存储块(帧),进程被划分成页–[[计算机科学概论原书第5版.pdf#page=238|计算机科学概论10.2.3页式内存管理]]![[Pasted image 20240809231833.png]]<ul><li><strong>帧(frame):</strong> 大小固定的小部分主存,用于存放进程页</li><li><strong>页(page):</strong> 大小固定的一部分进程,存储在内存帧中</li><li><strong>✨页映射表(Page Map Table, PMT):</strong> 操作系统用于记录页和帧直接的关系的表 –[[计算机科学概论原书第5版.pdf#page=239|计算机科学概论【页映射表】]]</li><li><strong>请求分页(demand paging):</strong> 页式内存管理法的拓展,只有当页面被引用(请求)时才会被载入内存<ul><li><strong>页面交换(page swap):</strong> 把一个页面从二级存储设备载入内存,通常会使另一个页面从内存中删除</li><li><strong>虚拟内存(virtual memory);</strong> 由于整个程序不必同时处于内存而造成的程序大小没有限制的假象</li><li><strong>系统颠簸(thrashing):</strong> 频繁的页面交换照成的低效处理</li></ul></li></ul></li></ul></li><li><strong>进程管理(process management)</strong>: 管理每个进程使用CPU的时间<ul><li><strong>进程状态(process state):</strong> 在操作系统的管理下,进程历经的概念性阶段![[Pasted image 20240809233933.png]]<ul><li><strong>创建</strong></li><li><strong>准备就绪</strong>:等待使用CPU</li><li><strong>运行</strong>:正在使用CPU</li><li><strong>等待</strong>:等待资源(处理CPU以外的资源)</li><li><strong>终止</strong>:进程硬件完成它的执行</li></ul></li><li><strong>[[进程控制块(process control block, PCB)]]:</strong> 操作系统管理进程信息使用的数据结构<ul><li><strong>上下文切换(context switch):</strong> 当一个进程移出CPU,另一个进程取代它时发生的寄存器信息交换</li></ul></li></ul></li><li><strong>CPU调度</strong>:确定哪个处于准备状态的进程移入运行状态<ul><li><strong>[[CPU调度算法]]</strong><ul><li><strong>非抢先调度(nonpreemptive scheduling):</strong> 当当前执行的进程自愿放弃了CPU时发生的CPU调度<ul><li><strong>先到先服务(FCFS)</strong></li><li><strong>最短作业优先(SJN)</strong></li></ul></li><li><strong>抢先调度(preemptive scheduling):</strong> 当操作系统决定照顾另一个进程而抢占当前执行进程的CPU资源时发生的CPU调度<ul><li><strong>轮询法(RR)</strong><ul><li><strong>时间片(time slice):</strong> 在CPU轮询算法中分配给每个进程的时间量</li></ul></li></ul></li><li><strong>周转周期(turnaround time):</strong> 从进程进入准备就绪状态到它最终完成之间的时间间隔,是评估CPU调度算法的标准</li></ul></li></ul></li></ul></li><li><strong>文件系统和目录</strong><ul><li><strong>文件系统(file system):</strong> 操作系统为它管理的文件提供的逻辑视图<ul><li><strong>文件(file)</strong>:数据的有名集合,用于组织二级存储设备<ul><li><strong>文本文件(text file):</strong> 包含字符(ASCII或Unicode字符集里的字符)的文件,使用<strong>文本编辑器</strong>便可以创建,查看和修改文本文件的内容<ul><li><strong>源文件:</strong> 高级语言编写的程序会被存储位文本文件</li></ul></li><li><strong>二进制文件(binary file):</strong> 包含特定格式的数据的文件,要求给位串一个特定的解释,得使用<strong>特定的解释器</strong>来阅读或修改二进制文件</li></ul></li><li><strong>文件类型(file type)</strong>:文件中存放的关于类型的信息</li><li><strong>文件拓展名(file extension)</strong>:文件名中<strong>说明</strong>文件类型的部分(只是说明,改变文件拓展名不会改变文件中的数据或它的内部格式)</li><li>![[Pasted image 20240810011448.png]]</li><li>[[计算机科学概论原书第5版.pdf#page=251|操作系统为不同文件类型根据文件拓展名匹配相应的解释器]]![[Pasted image 20240810011908.png]]</li><li><strong>文件操作</strong><ul><li><strong>具体的可执行操作</strong><ul><li><strong>创建文件</strong></li><li><strong>删除文件</strong></li><li><strong>打开文件</strong></li><li><strong>关闭文件</strong></li><li><strong>从文件中读取数据</strong></li><li><strong>把数据写入文件</strong></li><li><strong>重定位文件中的当前文件指针</strong></li><li><strong>把数据附加到文件结尾</strong></li><li><strong>删减文件(删除它的内容)</strong></li><li><strong>重命名文件</strong></li><li><strong>复制文件</strong></li></ul></li><li><strong>操作系统维护两个表</strong><ul><li>一个表说明哪些内存块是空的(也就是说可以用的)</li><li>为每个目录维护一个表,以记录该目录下的文件的信息</li></ul></li><li><strong>文件指针</strong><ul><li><strong>读指针(地址)</strong></li><li><strong>写指针(地址)</strong></li></ul></li><li><strong>[[文件访问(file access)]]</strong>![[Pasted image 20240810014048.png]]<ul><li><strong>顺序文件访问(sequential file access):</strong> 以线性方式访问文件中的数据的方式</li><li><strong>直接文件访问(direct file access):</strong> 通过指定逻辑记录编号直接访问文件中的数据方法</li></ul></li><li><strong>[[文件保护]]</strong></li></ul></li></ul></li></ul></li><li><strong>目录(directory):</strong> 文件的有名集合、![[Pasted image 20240810015125.png]]<ul><li><strong>目录树(directory tree):</strong> 展示文件系统的嵌套目录组织的结构</li><li><strong>根目录(root directory):</strong> 包含其他所有目录的最高层目录</li><li><strong>工作目录(working directory):</strong> 当前活动的子目录</li></ul></li><li><strong>路径名</strong><ul><li><strong>路径(path):</strong> 文件或子目录在文件系统中的位置的文本名字</li><li><strong>绝对路径(absolute path):</strong> 从根目录开始,包括后面所有后继子目录的路径</li><li><strong>相对路径(relative path):</strong> 从当前工作目录开始的路径</li></ul></li><li><strong>磁盘调度(disk scheduling):</strong> 决定先满足哪个磁盘I/O请求的操作<ul><li>先到先服务磁盘调度法</li><li>最短寻道时间优先磁盘调度法</li><li>SCAN磁盘调度法</li></ul></li></ul>]]></content>
<tags>
<tag> 读书笔记 </tag>
</tags>
</entry>
<entry>
<title>【计算机科学概论】硬件层</title>
<link href="/tangBlog/2024/08/08/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E6%A6%82%E8%AE%BA%E3%80%91%E7%A1%AC%E4%BB%B6%E5%B1%82/"/>
<url>/tangBlog/2024/08/08/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E6%A6%82%E8%AE%BA%E3%80%91%E7%A1%AC%E4%BB%B6%E5%B1%82/</url>
<content type="html"><![CDATA[<h1 id="硬件层"><a href="#硬件层" class="headerlink" title="硬件层"></a>硬件层</h1><p>硬件层从构成计算机的硬件角度研究计算机如何如何使用电信号来表示和操作二进制值</p><ul><li><strong>门和电路</strong><ul><li><strong>电信号</strong></li><li><strong>电平</strong></li><li><strong>[[门(gate)|门(gate)]]</strong>:对电信号执行基本运算的设备,介绍一个或多个输入信号,生产一个输出信号<ul><li>门的分类</li><li>门的构造</li></ul></li><li><strong>[[电路(circuit)]]</strong>:相互关联的门组合,用于实现特定的逻辑函数</li><li><strong>[[布尔代数(Boolean algebra)]]</strong></li></ul></li><li><strong>计算部件</strong>–[[计算机科学概论原书第5版.pdf#page=95|计算机科学概论第5章]]<ul><li><strong>[[计算机硬件]]</strong></li><li><strong>[[计算机科学概论原书第5版.pdf#page=99|计算机科学概论【5.2存储程序的概念】]]</strong><ul><li><strong>冯诺依曼体系结构</strong>![[Pasted image 20240807224846.png]]<ul><li><strong>存储单元</strong></li><li><strong>算数逻辑单元(Arithmetic/Logic Uint , ALU):</strong> 执行算数运算(加减乘除)和逻辑运算(俩个值的比较)的计算机部件<ul><li><strong>[[寄存器(register)]]</strong>:CPU中的小块存储区域,用于存储中间值或特殊数据</li></ul></li><li><strong>输入单元</strong></li><li><strong>输出单元</strong></li><li>✨<strong>控制单元(control unit)</strong> [[计算机科学概论原书第5版.pdf#page=103]] <ul><li><strong>特殊寄存器</strong><ul><li><strong>指令寄存器(Instruction Register , IR)</strong>:存放当前正在执行的指令的寄存器</li><li><strong>程序计数器(Program Counter , PC)</strong>:存放下一条要执行的指令的地址的寄存器</li></ul></li></ul></li><li><strong>CPU</strong>:算数逻辑单元和控制单元的组合</li></ul></li><li>✨<strong>读取-执行周期</strong>–[[计算机科学概论原书第5版.pdf#page=104|计算机科学概论5.2.2]]<ul><li>读取下一条指令</li><li>译解指令</li><li>如果需要,读取数据</li><li>执行指令</li></ul></li></ul></li><li><strong>嵌入式系统</strong>:作为大型系统的一部分,为完成小范围功能而专门设计的计算机.通常将一个嵌入式系统集成在单个微型处理器芯片上,程序被存储在ROM上<ul><li><strong>只读存储器(ROM,Read-Only Memory)</strong>:计算机中的一种非易失性存储器,这意味着即使在断电后,存储在其中的数据也不会丢失。ROM通常用于存储固件,即计算机在启动时所需的基本程序和操作系统的初始加载代码</li></ul></li><li><strong>并行体系结构</strong><ul><li><strong>并行计算</strong></li></ul></li></ul></li></ul>]]></content>
<tags>
<tag> 读书笔记 </tag>
</tags>
</entry>
<entry>
<title>【计算机科学概论】信息层</title>
<link href="/tangBlog/2024/08/07/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E6%A6%82%E8%AE%BA%E3%80%91%E4%BF%A1%E6%81%AF%E5%B1%82/"/>
<url>/tangBlog/2024/08/07/%E3%80%90%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E6%A6%82%E8%AE%BA%E3%80%91%E4%BF%A1%E6%81%AF%E5%B1%82/</url>
<content type="html"><![CDATA[<h1 id="信息层"><a href="#信息层" class="headerlink" title="信息层"></a>信息层</h1><p>信息层介绍计算机科学中如何理解和处理信息,通过数字化的方法将现实的模拟信号转化为数字信号.以二进制的形式记录</p><ul><li><p><strong>数字</strong></p><ul><li><strong>数的分类</strong><ul><li><strong>自然数:</strong> 从1开始的正整数(1, 2, 3, …)。有时也包括0(0, 1, 2, 3, …)。</li><li><strong>整数:</strong> 包括所有自然数、它们的负数以及0(… -3, -2, -1, 0, 1, 2, 3, …)。</li><li><strong>有理数:</strong> 可以表示为两个整数之比的数(a/b,其中a和b都是整数且b≠0)。所有整数都是有理数,因为它们可以表示为自身与1的比(如5可以表示为5/1)。</li><li><strong>实数:</strong> 包括所有有理数和无理数。无理数是不能表示为两个整数之比的数(如√2, π)。</li><li><strong>复数:</strong> 包括实数和虚数。形式为a + bi,其中a和b是实数,i是虚数单位,满足i² = -1。</li></ul></li><li><strong>基数(base)</strong><ul><li><strong>二进制计数系统</strong></li><li><strong>进制转换</strong></li></ul></li></ul></li><li><p><strong>数据表示法</strong></p><ul><li><strong>数据(data):</strong> 基本值或事实<ul><li><strong>模拟数据(analog data):</strong> 用连续形式表示的信息 </li><li><strong>数字数据(digital data):</strong> 用离散形式表示的信息</li></ul></li><li><strong>信息(infomation):</strong> 用有效的方式组织或处理过的数据</li><li><strong>多媒体数据类型</strong><ul><li><strong>数字</strong></li><li><strong>文本</strong></li><li><strong>音频</strong></li><li><strong>图像和图形</strong></li><li><strong>视频</strong></li></ul></li><li><strong>数据压缩(data compression)</strong><ul><li><strong>压缩率(compression ratio):</strong> 压缩后的数据大小除以原始数据大小的值,说明了压缩的程度</li><li><strong>无损压缩(lossless compression):</strong> 不会丢失信息的数据压缩技术</li><li><strong>有损压缩(lossy compression):</strong> 会丢失信息的数据压缩技术</li></ul></li><li><strong>数字数据表示法</strong><ul><li><strong>负数表示法</strong><ul><li><strong>符号数值表示法(signed-magnitude representation)</strong>: 通过+-符号的形式表示数所属的分类(正数或负数)</li><li><strong>定长量数</strong>:若只允许用定量的数值,则可以用一半数来表示正数,另一半数表示负数,符号由数的数值决定<ul><li>[[补码|补码(Two’s Complement)]]</li></ul></li></ul></li><li><strong>实数表示法</strong><ul><li><strong>浮点表示法(floating point):</strong> 表面了符号,尾数和指数的实数表示法</li><li><strong>科学计数法(scientific notation)</strong></li></ul></li><li><strong>文本表示法</strong><ul><li><strong>字符集(character set):</strong><ul><li><strong>ASCII字符集</strong></li><li><strong>Unicode字符集</strong></li></ul></li><li><strong>[[文本压缩]]</strong><ul><li><strong>关键字编码</strong></li><li><strong>行程程度编码</strong></li><li><strong>[[文本压缩#^c05359|赫夫曼编码]]</strong></li></ul></li></ul></li><li><strong>音频数据表示法</strong><ul><li><strong>[[音频数据格式]]</strong></li></ul></li><li><strong>图像和图像表示法</strong><ul><li><strong>颜色表示法</strong></li><li><strong>数字化图像和图形</strong><ul><li><strong>像素(pixel)</strong></li></ul></li><li><strong>图像的矢量表示法</strong><ul><li><strong>矢量图形</strong></li></ul></li></ul></li><li><strong>视频表示法</strong><ul><li><strong>[[视频格式]]</strong></li><li><strong>[[视频格式所涉及的技术]]</strong><ul><li><strong>编码方式</strong></li><li><strong>容器</strong></li><li><strong>压缩技术</strong></li></ul></li></ul></li></ul></li></ul></li></ul>]]></content>
<tags>
<tag> 读书笔记 </tag>
</tags>
</entry>
<entry>
<title>ios原生开发初体验</title>
<link href="/tangBlog/2024/07/30/ios%E5%8E%9F%E7%94%9F%E5%BC%80%E5%8F%91%E5%88%9D%E4%BD%93%E9%AA%8C/"/>
<url>/tangBlog/2024/07/30/ios%E5%8E%9F%E7%94%9F%E5%BC%80%E5%8F%91%E5%88%9D%E4%BD%93%E9%AA%8C/</url>
<content type="html"><![CDATA[<h1 id="ios原生开发初体验"><a href="#ios原生开发初体验" class="headerlink" title="ios原生开发初体验"></a>ios原生开发初体验</h1><h2 id="🍎前言"><a href="#🍎前言" class="headerlink" title="🍎前言"></a>🍎前言</h2><p>机缘巧合之下参加了“移动应用创新赛”,借此机会被迫主动接触ios应用的开发相关方面的知识与技术,也因此有了这篇文章,来记录下我的感受。从开始学习到这篇文章开始撰写,约一周时间,估摸着每天断断续续敲三四个小时左右,自认为收获颇丰。</p><h2 id="MacOS操作系统"><a href="#MacOS操作系统" class="headerlink" title="MacOS操作系统"></a>MacOS操作系统</h2><p>本打算直接谈Swift的,但不得不先提一嘴Mac,想要搞apple相关应用的开发就离不开要有台Mac(虚拟机除外)。我觉得这一步就难倒许多大学生了,显然目前国内的绝大多数大学生的操作系统是Windows。本人平时也是用习惯了Windows,一时间上手Mac真是哪哪都别扭,在这就折腾了好一会。</p><h2 id="Swift语言"><a href="#Swift语言" class="headerlink" title="Swift语言"></a>Swift语言</h2><p>我一上来就直接跟着apple官网的开发者教程走的,基础语法就嫖了眼菜鸟教程的目录,就按照官网教程直接开始敲代码了</p><h3 id="语法糖"><a href="#语法糖" class="headerlink" title="语法糖"></a>语法糖</h3><p>讲讲我喜欢的几个语法糖(不全),就一些语法看和python很像</p><ul><li><p><strong>换行:</strong>直接用换行来分割代码行,省去了敲<code>;</code>的步骤,必须点赞。我认为分号的意义在软件开发中就是为了恶心人,一行代码写完直接回车既能分割代码有能保持可读性,何乐而不为呢,没必要多此一举以<code>;</code>结尾。</p></li><li><p><strong>类型推断:</strong>这让定义变量时又可以少敲一点代码,像是Int、String、double、bool这些常用到的基础数据类型可以直接通过赋初值来由编译器自动判断</p><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> message <span class="operator">=</span> <span class="string">"Hello, World!"</span> <span class="comment">// 推断为 String 类型</span></span><br><span class="line"><span class="keyword">var</span> count <span class="operator">=</span> <span class="number">10</span> <span class="comment">// 推断为 Int 类型</span></span><br></pre></td></tr></table></figure></li><li><p><strong>简化 for-in 循环</strong>:简化了遍历数组和字典的语法。</p><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> fruits <span class="operator">=</span> [<span class="string">"apple"</span>, <span class="string">"banana"</span>, <span class="string">"cherry"</span>]</span><br><span class="line"><span class="keyword">for</span> fruit <span class="keyword">in</span> fruits {</span><br><span class="line"> <span class="built_in">print</span>(fruit)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> scores <span class="operator">=</span> [<span class="string">"Alice"</span>: <span class="number">90</span>, <span class="string">"Bob"</span>: <span class="number">85</span>]</span><br><span class="line"><span class="keyword">for</span> (name, score) <span class="keyword">in</span> scores {</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"<span class="subst">\(name)</span>: <span class="subst">\(score)</span>"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h3 id="语法特色"><a href="#语法特色" class="headerlink" title="语法特色"></a>语法特色</h3><ul><li><strong>可选类型:</strong>Swift 使用可选类型来处理可能为 nil 的值,避免了传统语言中的空指针异常。</li><li><strong>闭包(Closure)</strong></li></ul><h4 id="闭包(Closure)"><a href="#闭包(Closure)" class="headerlink" title="闭包(Closure)"></a><strong>闭包(Closure)</strong></h4><p>这对我来说也是一个新语法,还不知道能在哪里用到。</p><blockquote><p>一种能够捕获并存储其所在上下文中变量和常量的引用的函数。闭包不仅包含代码,还包含执行时所需的环境。闭包可以在其定义的作用域外被调用,并且能够访问和修改其捕获的变量。</p></blockquote><p>我发现它有个神奇的特性<em><strong>捕获环境</strong></em>:<em>闭包可以捕获并保存其上下文中的变量,即使这些变量在闭包定义的作用域外已不存在。</em></p><p>我就查了下捕获环境的实现机制:</p><ul><li><p><strong>捕获列表</strong>:闭包内创建变量的引用。</p></li><li><p><strong>堆存储</strong>:被捕获的变量在堆上存储,确保它们在闭包存活期间不会被释放。</p></li><li><p><strong>自动引用计数(ARC)</strong>:管理捕获变量的引用计数,确保变量在闭包使用期间不会被释放。</p></li></ul><ol><li><strong>捕获列表</strong></li></ol><p>当闭包捕获一个变量时,它实际上是在闭包内创建了该变量的一个引用。这个引用可以指向局部变量,也可以指向堆上的对象。捕获列表是闭包用来跟踪这些变量的结构。</p><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> total <span class="operator">=</span> <span class="number">0</span></span><br><span class="line"><span class="keyword">let</span> incrementer: () -> <span class="type">Int</span> <span class="operator">=</span> {</span><br><span class="line"> total <span class="operator">+=</span> incrementAmount</span><br><span class="line"> <span class="keyword">return</span> total</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在这个例子中,闭包捕获了 <code>total</code> 和 <code>incrementAmount</code>。这些变量被捕获为引用,闭包内可以通过这些引用访问和修改它们。</p><ol start="2"><li><strong>堆存储</strong></li></ol><p>当闭包捕获一个变量时,如果这个变量在闭包执行结束后仍需存在,那么这个变量会被存储在堆上,而不是栈上。这样可以确保即使闭包执行结束后,这些变量仍然存在。</p><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">makeIncrementer</span>(<span class="params">incrementAmount</span>: <span class="type">Int</span>) -> () -> <span class="type">Int</span> {</span><br><span class="line"> <span class="keyword">var</span> total <span class="operator">=</span> <span class="number">0</span></span><br><span class="line"> <span class="keyword">let</span> incrementer: () -> <span class="type">Int</span> <span class="operator">=</span> {</span><br><span class="line"> total <span class="operator">+=</span> incrementAmount</span><br><span class="line"> <span class="keyword">return</span> total</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> incrementer</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在这个函数中,当 <code>makeIncrementer</code> 返回时,<code>total</code> 和 <code>incrementAmount</code> 被捕获到堆上,因此 <code>incrementer</code> 闭包可以在函数返回后继续访问这些变量。</p><ol start="3"><li><strong>自动引用计数(ARC)</strong></li></ol><p>Swift 使用自动引用计数(ARC)来管理内存。当闭包捕获一个变量时,ARC 会增加这个变量的引用计数,确保变量在闭包存活期间不会被释放。当闭包不再使用时,ARC 会减少引用计数,并在没有其他引用时释放变量。</p><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> incrementByTwo <span class="operator">=</span> makeIncrementer(incrementAmount: <span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(incrementByTwo()) <span class="comment">// 输出 2</span></span><br><span class="line"><span class="built_in">print</span>(incrementByTwo()) <span class="comment">// 输出 4</span></span><br></pre></td></tr></table></figure><p>在这个例子中,<code>incrementByTwo</code> 持有 <code>incrementer</code> 闭包的引用,因此捕获的变量 <code>total</code> 和 <code>incrementAmount</code> 也不会被释放。当 <code>incrementByTwo</code> 被调用时,闭包中的代码可以继续访问和修改 <code>total</code> 和 <code>incrementAmount</code>。</p><h2 id="Xcode"><a href="#Xcode" class="headerlink" title="Xcode"></a>Xcode</h2><p>Xcode是Apple 官方开发应用程序工具和Android Studio类似,个人喜欢的点就coding的时候UI效果可以实时预览,便于页面的调整。而与我以往的Android开发感觉明显不同,我思考了一下觉得应该是采取的设计模式不同,一查也确实大致如此(MVC和MVVM的区别)</p><p>在查询资料的过程中也发现我的不足🤷♂️,其实现代Android开发也是推荐使用MVVM模式,同时也是可以通过Jetpack Compose 的预览功能类似于 SwiftUI,允许开发者在代码编写过程中实时查看 UI 变化。</p><p>下面就来看看MVVM和MVC究竟是什么吧👇</p><h3 id="MVC-和-MVVM"><a href="#MVC-和-MVVM" class="headerlink" title="MVC 和 MVVM"></a>MVC 和 MVVM</h3><p>MVC(Model-View-Controller)和 MVVM(Model-View-ViewModel)是两种常见的软件架构模式,它们主要用于分离代码中的关注点,从而使代码更加模块化、可维护和可测试。虽然它们都有类似的目的,但在具体实现和关注点上有明显的区别。</p><h4 id="MVC(Model-View-Controller)"><a href="#MVC(Model-View-Controller)" class="headerlink" title="MVC(Model-View-Controller)"></a>MVC(Model-View-Controller)</h4><p><strong>1. Model(模型):</strong></p><ul><li>表示应用的数据结构和业务逻辑。</li><li>与数据库和其他数据源交互。</li></ul><p><strong>2. View(视图):</strong></p><ul><li>负责显示数据和用户界面。</li><li>通过 Controller 获取数据,并根据这些数据更新视图。</li></ul><p><strong>3. Controller(控制器):</strong></p><ul><li>处理用户输入和应用逻辑。</li><li>作为 Model 和 View 之间的桥梁,接收用户输入并更新 Model,然后通知 View 更新显示。</li></ul><h4 id="MVVM(Model-View-ViewModel)"><a href="#MVVM(Model-View-ViewModel)" class="headerlink" title="MVVM(Model-View-ViewModel)"></a>MVVM(Model-View-ViewModel)</h4><p><strong>1. Model(模型):</strong></p><ul><li>表示应用的数据结构和业务逻辑。</li><li>与数据库和其他数据源交互。</li></ul><p><strong>2. View(视图):</strong></p><ul><li>负责显示数据和用户界面。</li><li>通过绑定(Binding)直接获取 ViewModel 提供的数据,并根据这些数据更新视图。</li></ul><p><strong>3. ViewModel(视图模型):</strong></p><ul><li>负责处理业务逻辑和准备数据供 View 显示。</li><li>不直接引用 View,而是通过绑定将数据和命令暴露给 View。</li></ul><h4 id="MVC-与-MVVM-的主要区别"><a href="#MVC-与-MVVM-的主要区别" class="headerlink" title="MVC 与 MVVM 的主要区别"></a>MVC 与 MVVM 的主要区别</h4><p><strong>1. 关注点的分离:</strong></p><ul><li>在 MVC 中,Controller 通常会变得非常复杂,因为它需要处理很多视图和业务逻辑。</li><li>在 MVVM 中,ViewModel 处理业务逻辑和数据准备,视图只负责展示,通过绑定机制从 ViewModel 获取数据。这使得视图更加轻量和专注。</li></ul><p><strong>2. 数据绑定:</strong></p><ul><li>MVC 没有内置的数据绑定机制。Controller 必须显式地将数据推送到 View。</li><li>MVVM 中,数据绑定是核心特性。视图绑定到 ViewModel 的属性,当属性发生变化时,视图会自动更新。</li></ul><p><strong>3. 测试:</strong></p><ul><li>MVC 中,Controller 和 View 耦合紧密,测试起来比较困难,特别是单元测试。</li><li>MVVM 中,ViewModel 是与视图无关的纯 Swift 类,容易进行单元测试。</li></ul><p><strong>4. 依赖关系:</strong></p><ul><li>MVC 中,View 和 Controller 之间存在直接依赖,Controller 需要知道 View 的具体实现。</li><li>MVVM 中,View 和 ViewModel 通过绑定和协议进行解耦,View 不直接依赖 ViewModel 的具体实现。</li></ul><h3 id="SwiftUI"><a href="#SwiftUI" class="headerlink" title="SwiftUI"></a>SwiftUI</h3><p>这是我最最最想夸的点,在我整个学习和实操过程中最佩服的就是苹果的美学思想,实在是太优美了,简洁又高效,感觉轻轻松松就能做出果味十足的各种界面,这在提升审美方面有很大帮助。在视图部分的制作过程中主要就是用到了SwiftUI,目的类似于Android开发中的xml,下面提一下我喜欢的优点。</p><ul><li><strong>三维画布:</strong>通过HStack(Horizontal)、VStack(Vertical)、ZStack(我也布吉岛单词是哪个)等等通用布局来实现复杂的UI效果,ZStack我也是第一次见,像是把布局从二维的画板添加了Z轴,拔高到了三维,有了前后关系。</li><li><strong>动态布局:</strong>在确定不同组件(image、button、Text之类)和布局尺寸时可以很方便的根据屏幕的尺寸动态改变,且属性命名也很统一符合直觉。Space()也非常好用,能自动占满剩余空间。</li><li><strong>动画优美:</strong>基础动画效果的实现可以很简单,通过几行代码就能实现果味十足的各种动画效果。</li></ul><h3 id="资源管理"><a href="#资源管理" class="headerlink" title="资源管理"></a>资源管理</h3><p>我也特别喜欢Xcode中对图片、颜色等资源的集中管理,<strong>色彩集</strong>的使用可以很好的实现昼夜模式颜色的切换,用起来也相当顺手可以用各种格式定义颜色。</p><h2 id="🍎后记"><a href="#🍎后记" class="headerlink" title="🍎后记"></a>🍎后记</h2><p>通过总结反思ios学习的体验让我可以更好的体悟苹果公司的技术思想,当然我这篇不过是些许皮毛,当对我而言已是受益匪浅。</p><p>我这几天的ios应用开发体验感觉实属愉悦,也是长了见识,真心建议任何对移动应用开发方面的人可以大胆试试,感受苹果公司的软件开发智慧。</p>]]></content>
</entry>
<entry>
<title>HTML学习</title>
<link href="/tangBlog/2024/07/02/HTML%E5%AD%A6%E4%B9%A0/"/>
<url>/tangBlog/2024/07/02/HTML%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<h1 id="HTML基础学习记录"><a href="#HTML基础学习记录" class="headerlink" title="HTML基础学习记录"></a>HTML基础学习记录</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>知识是互通的嘛,个人粗浅的感觉就是Android中xml和markdown的杂交(这么说可能是有点倒反天罡了,但毕竟本人接触前两者更早)入门倒是挺快的,也感觉没啥好系统写的。</p><h2 id="学习资料"><a href="#学习资料" class="headerlink" title="学习资料"></a>学习资料</h2><p><a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started">HTML 入门 - 学习 Web 开发 |MDN的 — Getting started with HTML - Learn web development | MDN (mozilla.org)</a></p><p><a href="https://www.runoob.com/html/html-tutorial.html">HTML 教程 | 菜鸟教程 (runoob.com)</a></p><p><a href="https://github.com/denysdovhan/learnyouhtml?tab=readme-ov-file">denysdovhan/learnyouhtml: Learn you how to create your first web page (github.com)</a>👈英文好的可以试试这个,边学边练</p><h2 id="什么是HTML"><a href="#什么是HTML" class="headerlink" title="什么是HTML"></a>什么是HTML</h2><p>HTML(HyperText Markup Language,超文本标记语言)是一种用于创建网页的标准标记语言。它被用来描述网页的结构和内容。HTML使用标记标签(tags)来定义不同的元素,如标题、段落、链接、图片等。每个标签都以尖括号(< 和 >)包围,并通常成对出现,一个开始标签和一个结束标签。</p><p><strong>HyperText(超文本)</strong>:</p><ul><li><strong>超文本</strong> 是一种文本,它不仅仅是线性的,用户可以通过点击链接在不同的文档之间跳转。这种能力使得信息在互联网上以网络结构呈现,而不是简单的线性结构。</li></ul><p><strong>Markup(标记)</strong>:</p><ul><li><strong>标记</strong> 指的是使用标签(tags)来标记文本的不同部分。HTML 使用标签来定义网页的结构和内容,例如标题、段落、链接、图像等。</li><li>这些标签帮助浏览器理解和呈现网页内容。</li></ul><p><strong>Language(语言)</strong>:</p><ul><li><strong>语言</strong> 表明 HTML 不是一种编程语言,而是一种标记语言(markup language)。它使用预定义的标记标签来描述网页内容的结构和呈现。</li></ul><h2 id="相关专业术语"><a href="#相关专业术语" class="headerlink" title="相关专业术语"></a>相关专业术语</h2><ul><li><p><strong>元素(Element)</strong>:由开始标签、内容和结束标签组成的HTML结构。例如,<code><p>这是一个段落</p></code> 是一个段落元素。</p></li><li><p><strong>标签(Tag)</strong>:用尖括号包围的HTML标记,用于定义HTML元素。例如,<code><p></code> 是段落标签。</p></li><li><p><strong>属性(Attribute)</strong>:用于提供有关元素的附加信息,位于开始标签内。例如,<code><a href="https://www.example.com">链接</a></code> 中的 <code>href</code> 是一个属性,定义了链接的目标。</p></li><li><p><strong>块级元素(Block-level Element)</strong>:在页面上占据整个行的元素,常用于布局。例如,<code><div></code>, <code><p></code>, <code><h1></code> 等。</p></li><li><p><strong>行内元素(Inline Element)</strong>:仅占据其内容所需空间的元素,常用于文本格式化。例如,<code><span></code>, <code><a></code>, <code><img></code> 等。</p></li></ul><h2 id="基础语法"><a href="#基础语法" class="headerlink" title="基础语法"></a>基础语法</h2><h3 id="基础语法-1"><a href="#基础语法-1" class="headerlink" title="基础语法"></a>基础语法</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>HTML 基础语法示例<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="comment"><!-- 外部css --></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"styles.css"</span>></span></span><br><span class="line"> <span class="comment"><!-- 内联css --></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.inline-style</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">color</span>: blue;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-weight</span>: bold;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="comment"><!-- 标题和段落 --></span></span><br><span class="line"> <span class="tag"><<span class="name">header</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span> <span class="attr">class</span>=<span class="string">"inline-style"</span>></span>这是一级标题<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>这是二级标题<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一个段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">header</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 超链接和图片 --></span></span><br><span class="line"> <span class="tag"><<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一个包含<span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"https://www.example.com"</span>></span>链接<span class="tag"></<span class="name">a</span>></span>的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一个包含图片的段落:<span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"example.jpg"</span> <span class="attr">alt</span>=<span class="string">"示例图片"</span>></span><span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 列表 --></span></span><br><span class="line"> <span class="tag"><<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>无序列表<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 1<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 2<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 3<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ul</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>有序列表<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ol</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 1<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 2<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 3<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ol</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 表格 --></span></span><br><span class="line"> <span class="tag"><<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>表格<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">table</span> <span class="attr">border</span>=<span class="string">"1"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">th</span>></span>列 1<span class="tag"></<span class="name">th</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">th</span>></span>列 2<span class="tag"></<span class="name">th</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>单元格 1<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>单元格 2<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>单元格 3<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">td</span>></span>单元格 4<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">tr</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">table</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 表单 --></span></span><br><span class="line"> <span class="tag"><<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>表单<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">form</span> <span class="attr">action</span>=<span class="string">"submit_form.php"</span> <span class="attr">method</span>=<span class="string">"post"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"name"</span>></span>姓名:<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"name"</span> <span class="attr">name</span>=<span class="string">"name"</span>></span><span class="tag"><<span class="name">br</span>></span><span class="tag"><<span class="name">br</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"email"</span>></span>电子邮件:<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"email"</span> <span class="attr">id</span>=<span class="string">"email"</span> <span class="attr">name</span>=<span class="string">"email"</span>></span><span class="tag"><<span class="name">br</span>></span><span class="tag"><<span class="name">br</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"message"</span>></span>留言:<span class="tag"></<span class="name">label</span>></span><span class="tag"><<span class="name">br</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">textarea</span> <span class="attr">id</span>=<span class="string">"message"</span> <span class="attr">name</span>=<span class="string">"message"</span> <span class="attr">rows</span>=<span class="string">"4"</span> <span class="attr">cols</span>=<span class="string">"50"</span>></span><span class="tag"></<span class="name">textarea</span>></span><span class="tag"><<span class="name">br</span>></span><span class="tag"><<span class="name">br</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> <span class="attr">value</span>=<span class="string">"提交"</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">form</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 媒体元素 --></span></span><br><span class="line"> <span class="tag"><<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>音频和视频<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">audio</span> <span class="attr">controls</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">source</span> <span class="attr">src</span>=<span class="string">"audio.mp3"</span> <span class="attr">type</span>=<span class="string">"audio/mpeg"</span>></span></span><br><span class="line"> 您的浏览器不支持音频元素。</span><br><span class="line"> <span class="tag"></<span class="name">audio</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">video</span> <span class="attr">controls</span> <span class="attr">width</span>=<span class="string">"320"</span> <span class="attr">height</span>=<span class="string">"240"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">source</span> <span class="attr">src</span>=<span class="string">"video.mp4"</span> <span class="attr">type</span>=<span class="string">"video/mp4"</span>></span></span><br><span class="line"> 您的浏览器不支持视频元素。</span><br><span class="line"> <span class="tag"></<span class="name">video</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 内联脚本 --></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'DOMContentLoaded'</span>, <span class="keyword">function</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'h1'</span>).<span class="property">textContent</span> = <span class="string">'这是使用 JavaScript 修改的标题'</span>;</span></span><br><span class="line"><span class="language-javascript"> });</span></span><br><span class="line"><span class="language-javascript"> </span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 外部脚本 --></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"script.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p><strong>标题和段落</strong>:</p><ul><li>使用 <code><h1></code> 到 <code><h6></code> 标签定义标题。</li><li>使用 <code><p></code> 标签定义段落。</li></ul><p><strong>链接和图片</strong>:</p><ul><li>使用 <code><a></code> 标签创建超链接。</li><li>使用 <code><img></code> 标签嵌入图像,<code>src</code> 属性指定图像路径,<code>alt</code> 属性提供替代文本。</li></ul><p><strong>列表</strong>:</p><ul><li>使用 <code><ul></code> 和 <code><ol></code> 标签创建无序列表和有序列表。</li><li>使用 <code><li></code> 标签定义列表项。</li></ul><p><strong>表格</strong>:</p><ul><li>使用 <code><table></code> 标签创建表格,<code><tr></code> 定义行,<code><th></code> 和 <code><td></code> 定义表头和单元格。</li></ul><p><strong>表单</strong>:</p><ul><li>使用 <code><form></code> 标签创建表单。</li><li>使用 <code><input></code>、<code><textarea></code> 和 <code><label></code> 标签定义表单控件。</li></ul><p><strong>媒体元素</strong>:</p><ul><li>使用 <code><audio></code> 和 <code><video></code> 标签嵌入音频和视频。</li></ul><p><strong>内联样式和外部样式表</strong>:</p><ul><li>使用 <code><style></code> 标签定义内联样式。</li><li>使用 <code><link></code> 标签链接外部样式表。</li></ul><p><strong>脚本</strong>:</p><ul><li>使用 <code><script></code> 标签嵌入内联脚本或引用外部脚本。</li></ul><h3 id="文本格式化"><a href="#文本格式化" class="headerlink" title="文本格式化"></a>文本格式化</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>常用文本格式化示例<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">body</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-family</span>: Arial, sans-serif;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin</span>: <span class="number">20px</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">h1</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">text-align</span>: center;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">color</span>: <span class="number">#333</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">p</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin</span>: <span class="number">10px</span> <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.highlight</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background-color</span>: yellow;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>HTML 常用文本格式化示例<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 粗体和斜体 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一段包含<span class="tag"><<span class="name">strong</span>></span>粗体<span class="tag"></<span class="name">strong</span>></span>和<span class="tag"><<span class="name">em</span>></span>斜体<span class="tag"></<span class="name">em</span>></span>文本的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 粗体和斜体的另一种表示方式 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一段包含<span class="tag"><<span class="name">b</span>></span>粗体<span class="tag"></<span class="name">b</span>></span>和<span class="tag"><<span class="name">i</span>></span>斜体<span class="tag"></<span class="name">i</span>></span>文本的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 下划线和删除线 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一段包含<span class="tag"><<span class="name">u</span>></span>下划线<span class="tag"></<span class="name">u</span>></span>和<span class="tag"><<span class="name">del</span>></span>删除线<span class="tag"></<span class="name">del</span>></span>文本的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 上标和下标 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一段包含<span class="tag"><<span class="name">sup</span>></span>上标<span class="tag"></<span class="name">sup</span>></span>和<span class="tag"><<span class="name">sub</span>></span>下标<span class="tag"></<span class="name">sub</span>></span>文本的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 代码文本 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一段包含<span class="tag"><<span class="name">code</span>></span>代码文本<span class="tag"></<span class="name">code</span>></span>的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 引用 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一段包含<span class="tag"><<span class="name">q</span>></span>短引用<span class="tag"></<span class="name">q</span>></span>的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">blockquote</span>></span></span><br><span class="line"> 这是一段块引用,用于引用较长的文本。</span><br><span class="line"> <span class="tag"></<span class="name">blockquote</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 缩写 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一个包含缩写的段落:<span class="tag"><<span class="name">abbr</span> <span class="attr">title</span>=<span class="string">"Hypertext Markup Language"</span>></span>HTML<span class="tag"></<span class="name">abbr</span>></span><span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 引用地址 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一个包含引用地址的段落:<span class="tag"><<span class="name">cite</span>></span>《HTML & CSS: Design and Build Websites》<span class="tag"></<span class="name">cite</span>></span><span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 变量 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一个包含变量的段落:<span class="tag"><<span class="name">var</span>></span>x<span class="tag"></<span class="name">var</span>></span> + <span class="tag"><<span class="name">var</span>></span>y<span class="tag"></<span class="name">var</span>></span> = <span class="tag"><<span class="name">var</span>></span>z<span class="tag"></<span class="name">var</span>></span><span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 预格式化文本 --></span></span><br><span class="line"> <span class="tag"><<span class="name">pre</span>></span></span><br><span class="line">这是一些预格式化的文本。</span><br><span class="line">它保留了所有的空格和换行。</span><br><span class="line"> <span class="tag"></<span class="name">pre</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 高亮 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一段包含<span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">"highlight"</span>></span>高亮文本<span class="tag"></<span class="name">span</span>></span>的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"><!-- 地址 --></span></span><br><span class="line"> <span class="tag"><<span class="name">address</span>></span></span><br><span class="line"> 这是一个地址块:<span class="tag"><<span class="name">br</span>></span></span><br><span class="line"> John Doe<span class="tag"><<span class="name">br</span>></span></span><br><span class="line"> 1234 Main St<span class="tag"><<span class="name">br</span>></span></span><br><span class="line"> Springfield, IL 62704<span class="tag"><<span class="name">br</span>></span></span><br><span class="line"> USA</span><br><span class="line"> <span class="tag"></<span class="name">address</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 标记 --></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一段包含<span class="tag"><<span class="name">mark</span>></span>标记<span class="tag"></<span class="name">mark</span>></span>文本的段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="E:\markdown\img\image-20240702220942808.png" alt="image-20240702220942808"></p><p><strong>粗体和斜体</strong>:</p><ul><li><code><strong></code> 和 <code><em></code> 用于语义上的重要性和强调。</li><li><code><b></code> 和 <code><i></code> 用于视觉上的粗体和斜体效果。</li></ul><p><strong>下划线和删除线</strong>:</p><ul><li><code><u></code> 用于下划线。</li><li><code><del></code> 用于删除线。</li></ul><p><strong>上标和下标</strong>:</p><ul><li><code><sup></code> 用于上标。</li><li><code><sub></code> 用于下标。</li></ul><p><strong>代码文本</strong>:</p><ul><li><code><code></code> 用于表示代码片段。</li></ul><p><strong>引用</strong>:</p><ul><li><code><q></code> 用于短引用,通常会自动添加引号。</li><li><code><blockquote></code> 用于块引用,通常用于长段引用。</li></ul><p><strong>缩写</strong>:</p><ul><li><code><abbr></code> 用于缩写,<code>title</code> 属性提供完整的描述。</li></ul><p><strong>引用地址</strong>:</p><ul><li><code><cite></code> 用于引用书名、文章名等。</li></ul><p><strong>变量</strong>:</p><ul><li><code><var></code> 用于表示变量。</li></ul><p><strong>预格式化文本</strong>:</p><ul><li><code><pre></code> 用于保留文本中的空格和换行。</li></ul><p><strong>高亮</strong>:</p><ul><li>使用 <code><span></code> 和 CSS 类来实现高亮效果。</li></ul><p><strong>地址</strong>:</p><ul><li><code><address></code> 用于表示联系信息或地址。</li></ul><p><strong>标记</strong>:</p><ul><li><code><mark></code> 用于高亮显示文本,通常用于表示搜索结果中的匹配项。</li></ul><h3 id="块级元素和行内元素"><a href="#块级元素和行内元素" class="headerlink" title="块级元素和行内元素"></a>块级元素和行内元素</h3><h4 id="块级元素的特点"><a href="#块级元素的特点" class="headerlink" title="块级元素的特点"></a>块级元素的特点</h4><ol><li><strong>独占一行</strong>:块级元素通常从新的一行开始,并且会独占一行。</li><li><strong>宽度自动填充父元素</strong>:块级元素的宽度默认会自动填充它的父元素的宽度。</li><li><strong>可以包含其他块级元素和内联元素</strong>:块级元素内部可以包含其他块级元素和内联元素。</li><li><strong>常用于布局</strong>:块级元素通常用于创建页面布局结构。</li></ol><h4 id="内联元素的特点"><a href="#内联元素的特点" class="headerlink" title="内联元素的特点"></a>内联元素的特点</h4><ol><li><strong>不独占一行</strong>:内联元素不会从新的一行开始,它们与相邻的内联元素和文本内容在同一行内显示。</li><li><strong>宽度随内容变化</strong>:内联元素的宽度根据其内容的宽度变化,不会像块级元素那样自动填满父元素的宽度。</li><li><strong>只能包含内联元素</strong>:内联元素通常只能包含其他内联元素或文本,不能包含块级元素。</li></ol> <figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>块级元素示例<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="language-css"></span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">body</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">font-family</span>: Arial, sans-serif;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin</span>: <span class="number">20px</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> <span class="selector-class">.container</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#ccc</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">padding</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">header</span>, <span class="selector-tag">footer</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">background-color</span>: <span class="number">#f4f4f4</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">padding</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin-bottom</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">section</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#ddd</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin-bottom</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">padding</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> <span class="selector-tag">article</span> {</span></span><br><span class="line"><span class="language-css"> <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#eee</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">margin-bottom</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css"> <span class="attribute">padding</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css"> }</span></span><br><span class="line"><span class="language-css"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">header</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>块级元素示例<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">header</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>章节 1<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是一个段落。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">article</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>文章 1<span class="tag"></<span class="name">h3</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是文章 1 的内容。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">article</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">article</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h3</span>></span>文章 2<span class="tag"></<span class="name">h3</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是文章 2 的内容。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">article</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>章节 2<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 1<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 2<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span>列表项 3<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">section</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">footer</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>这是页脚内容。<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">footer</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="常见的块级元素"><a href="#常见的块级元素" class="headerlink" title="常见的块级元素"></a>常见的块级元素</h4><ol><li>**<div>**:通用容器,用于组合内容和创建布局。</li><li>**<p>**:段落,用于包含文本段落。</li><li><strong><h1></strong> 到 **<h6>**:标题标签,用于定义六级标题。</li><li><strong><ul></strong> 和 **<ol>**:无序列表和有序列表,用于创建列表。</li><li>**<li>**:列表项,用于列表中的每一项。</li><li>**<table>**:表格,用于显示表格数据。</li><li>**<header>**:页面或区块的头部,用于包含介绍内容或导航链接。</li><li>**<footer>**:页面或区块的底部,用于包含作者信息、版权声明或导航链接。</li><li>**<section>**:文档的区段,用于划分页面内容的不同部分。</li><li>**<article>**:独立的内容片段,用于表示文章、博文、评论等。</li></ol><h4 id="常见的内联元素"><a href="#常见的内联元素" class="headerlink" title="常见的内联元素"></a>常见的内联元素</h4><ol><li><strong><a></strong> - Anchor(锚点)</li><li><strong><abbr></strong> - Abbreviation(缩写)</li><li><strong><b></strong> - Bold(粗体)</li><li><strong><bdi></strong> - Bidirectional Isolate(双向隔离)</li><li><strong><bdo></strong> - Bidirectional Override(双向覆盖)</li><li><strong><br></strong> - Break(换行)</li><li><strong><cite></strong> - Citation(引用)</li><li><strong><code></strong> - Code(代码)</li><li><strong><dfn></strong> - Definition(定义)</li><li><strong><em></strong> - Emphasis(强调)</li><li><strong><i></strong> - Italic(斜体)</li><li><strong><img></strong> - Image(图像)</li><li><strong><input></strong> - Input(输入)</li><li><strong><kbd></strong> - Keyboard Input(键盘输入)</li><li><strong><label></strong> - Label(标签)</li><li><strong><mark></strong> - Mark(标记)</li><li><strong><q></strong> - Quote(短引用)</li><li><strong><s></strong> - Strikethrough(删除线)</li><li><strong><samp></strong> - Sample Output(示例输出)</li><li><strong><small></strong> - Small Text(小号文本)</li><li><strong><span></strong> - Span(跨度)</li><li><strong><strong></strong> - Strong Emphasis(强烈强调)</li><li><strong><sub></strong> - Subscript(下标)</li><li><strong><sup></strong> - Superscript(上标)</li><li><strong><time></strong> - Time(时间)</li><li><strong><u></strong> - Underline(下划线)</li><li><strong><var></strong> - Variable(变量)</li></ol><h2 id="来学学英语单词"><a href="#来学学英语单词" class="headerlink" title="来学学英语单词"></a>来学学英语单词</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><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></pre></td><td class="code"><pre><span class="line">1. **<a>** - Anchor(锚点)</span><br><span class="line">2. **<abbr>** - Abbreviation(缩写)</span><br><span class="line">3. **<address>** - Address(地址)</span><br><span class="line">4. **<article>** - Article(文章)</span><br><span class="line">5. **<aside>** - Aside(旁注)</span><br><span class="line">6. **<audio>** - Audio(音频)</span><br><span class="line">7. **<b>** - Bold(粗体)</span><br><span class="line">8. **<bdi>** - Bidirectional Isolate(双向隔离)</span><br><span class="line">9. **<blockquote>** - Block Quote(块引用)</span><br><span class="line">10. **<body>** - Body(主体)</span><br><span class="line">11. **<br>**Break(换行)</span><br><span class="line">12. **<button>** - Button(按钮)</span><br><span class="line">13. **<canvas>** - Canvas(画布)</span><br><span class="line">14. **<caption>** - Caption(标题)</span><br><span class="line">15. **<cite>** - Citation(引用)</span><br><span class="line">16. **<code>** - Code(代码)</span><br><span class="line">17. **<col>** - Column(列)</span><br><span class="line">18. **<colgroup>** - Column Group(列组)</span><br><span class="line">19. **<data>** - Data(数据)</span><br><span class="line">20. **<datalist>** - Data List(数据列表)</span><br><span class="line">21. **<dd>** - Description Definition(定义描述)</span><br><span class="line">22. **<del>** - Deleted Text(删除文本)</span><br><span class="line">23. **<details>** - Details(细节)</span><br><span class="line">24. **<dfn>** - Definition(定义)</span><br><span class="line">25. **<dialog>** - Dialog(对话框)</span><br><span class="line">26. **<div>** - Division(分区)</span><br><span class="line">27. **<dl>** - Description List(定义列表)</span><br><span class="line">28. **<dt>** - Description Term(定义术语)</span><br><span class="line">29. **<em>** - Emphasis(强调)</span><br><span class="line">30. **<embed>** - Embed(嵌入)</span><br><span class="line">31. **<fieldset>** - Field Set(字段集)</span><br><span class="line">32. **<figcaption>** - Figure Caption(图表标题)</span><br><span class="line">33. **<figure>** - Figure(图表)</span><br><span class="line">34. **<footer>** - Footer(页脚)</span><br><span class="line">35. **<form>** - Form(表单)</span><br><span class="line">36. **<h1>** to **<h6>** - Heading 1 to Heading 6(标题1到标题6)</span><br><span class="line">37. **<head>** - Head(头部)</span><br><span class="line">38. **<header>** - Header(页头)</span><br><span class="line">39. **<hr>** - Horizontal Rule(水平线)</span><br><span class="line">40. **<html>** - HyperText Markup Language(超文本标记语言)</span><br><span class="line">41. **<i>** - Italic(斜体)</span><br><span class="line">42. **<iframe>** - Inline Frame(内联框架)</span><br><span class="line">43. **<img>** - Image(图像)</span><br><span class="line">44. **<input>** - Input(输入)</span><br><span class="line">45. **<ins>** - Inserted Text(插入文本)</span><br><span class="line">46. **<kbd>** - Keyboard Input(键盘输入)</span><br><span class="line">47. **<label>** - Label(标签)</span><br><span class="line">48. **<legend>** - Legend(图例)</span><br><span class="line">49. **<li>** - List Item(列表项)</span><br><span class="line">50. **<link>** - Link(链接)</span><br><span class="line">51. **<main>** - Main Content(主要内容)</span><br><span class="line">52. **<map>** - Image Map(图像地图)</span><br><span class="line">53. **<mark>** - Marked Text(标记文本)</span><br><span class="line">54. **<meta>** - Meta Information(元信息)</span><br><span class="line">55. **<meter>** - Meter(计量)</span><br><span class="line">56. **<nav>** - Navigation(导航)</span><br><span class="line">57. **<noscript>** - No Script(无脚本)</span><br><span class="line">58. **<object>** - Object(对象)</span><br><span class="line">59. **<ol>** - Ordered List(有序列表)</span><br><span class="line">60. **<optgroup>** - Option Group(选项组)</span><br><span class="line">61. **<option>** - Option(选项)</span><br><span class="line">62. **<output>** - Output(输出)</span><br><span class="line">63. **<p>** - Paragraph(段落)</span><br><span class="line">64. **<picture>** - Picture(图片)</span><br><span class="line">65. **<pre>** - Preformatted Text(预格式化文本)</span><br><span class="line">66. **<progress>** - Progress(进度)</span><br><span class="line">67. **<q>** - Quote(引用)</span><br><span class="line">68. **<rp>** - Ruby Parenthesis(注释括号)</span><br><span class="line">69. **<rt>** - Ruby Text(注释文本)</span><br><span class="line">70. **<ruby>** - Ruby Annotation(注释)</span><br><span class="line">71. **<s>** - Strikethrough(删除线)</span><br><span class="line">72. **<samp>** - Sample Output(示例输出)</span><br><span class="line">73. **<script>** - Script(脚本)</span><br><span class="line">74. **<section>** - Section(节)</span><br><span class="line">75. **<select>** - Select List(选择列表)</span><br><span class="line">76. **<small>** - Small Text(小号文本)</span><br><span class="line">77. **<source>** - Source(来源)</span><br><span class="line">78. **<span>** - Span(跨度)</span><br><span class="line">79. **<strong>** - Strong Emphasis(强烈强调)</span><br><span class="line">80. **<style>** - Style(样式)</span><br><span class="line">81. **<sub>** - Subscript(下标)</span><br><span class="line">82. **<summary>** - Summary(摘要)</span><br><span class="line">83. **<sup>** - Superscript(上标)</span><br><span class="line">84. **<table>** - Table(表格)</span><br><span class="line">85. **<tbody>** - Table Body(表格主体)</span><br><span class="line">86. **<td>** - Table Data(表格数据)</span><br><span class="line">87. **<template>** - Template(模板)</span><br><span class="line">88. **<textarea>** - Text Area(文本区域)</span><br><span class="line">89. **<tfoot>** - Table Footer(表格脚部)</span><br><span class="line">90. **<th>** - Table Header(表头)</span><br><span class="line">91. **<thead>** - Table Head(表头部)</span><br><span class="line">92. **<time>** - Time(时间)</span><br><span class="line">93. **<title>** - Title(标题)</span><br><span class="line">94. **<tr>** - Table Row(表格行)</span><br><span class="line">95. **<track>** - Text Track(文本轨道)</span><br><span class="line">96. **<u>** - Underline(下划线)</span><br><span class="line">97. **<ul>** - Unordered List(无序列表)</span><br><span class="line">98. **<var>** - Variable(变量)</span><br><span class="line">99. **<video>** - Video(视频)</span><br><span class="line">100. **<wbr>** - Word Break Opportunity(单词断点)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>个人感觉核心知识不多,都是些细枝末节的记忆点,学起来很快,主要还是要尽快在实践中深化理解</p>]]></content>
</entry>
<entry>
<title>Git本地与远程</title>
<link href="/tangBlog/2024/06/14/Git%E6%9C%AC%E5%9C%B0%E4%B8%8E%E8%BF%9C%E7%A8%8B/"/>
<url>/tangBlog/2024/06/14/Git%E6%9C%AC%E5%9C%B0%E4%B8%8E%E8%BF%9C%E7%A8%8B/</url>
<content type="html"><![CDATA[<h1 id="Git本地与远程"><a href="#Git本地与远程" class="headerlink" title="Git本地与远程"></a>Git本地与远程</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>虽然用了github好久了,也学过一遍git但在项目实操的时候还是遇到好多问题,于是痛定思痛写下这篇文章结合实践中暴露出的毛病来重新学习学习Git,问题主要集中在远程仓库方面。</p><h2 id="美化下git"><a href="#美化下git" class="headerlink" title="美化下git"></a>美化下git</h2><p>工欲善其事必先利其器,磨刀不误砍柴工,配置下branch相关的颜色,更好康些,用起来也更舒服😁</p><p>git中输入</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git config --global --edit</span><br></pre></td></tr></table></figure><p>在里面加上这个👇</p> <figure class="highlight shell"><table><tr><td class="gutter"><pre><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">[color "branch"]</span><br><span class="line"> current = yellow reverse</span><br><span class="line"> local = green bold</span><br><span class="line"> remote = cyan ul</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>:wq保存退出后,看下效果吧</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git branch -v</span></span><br></pre></td></tr></table></figure><p>这样就把当前分支设置为好看的黄色,远端分支设置成青色,当地分支设为绿色o( ̄▽ ̄)d</p><hr><p>你也可以按自己喜好来配置</p><p>以下是如何在 Git 配置文件中应用这些样式的示例:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">[color "branch"]</span><br><span class="line"> current = yellow bold</span><br><span class="line"> local = green dim</span><br><span class="line"> remote = cyan ul</span><br><span class="line"> plain = white blink</span><br><span class="line"> upstream = magenta reverse</span><br><span class="line"> gone = red italic</span><br></pre></td></tr></table></figure><p><strong>颜色</strong>👇</p><p><strong>normal</strong>:默认终端颜色</p><p><strong>black</strong>:黑色文字</p><p><strong>red</strong>:红色文字</p><p><strong>green</strong>:绿色文字</p><p><strong>yellow</strong>:黄色文字</p><p><strong>blue</strong>:蓝色文字</p><p><strong>magenta</strong>:洋红色文字</p><p><strong>cyan</strong>:青色文字</p><p><strong>white</strong>:白色文字</p><p><strong>样式</strong>👇</p><p><strong>bold</strong>:加粗</p><p><strong>dim</strong>:暗淡</p><p><strong>ul</strong>:下划线</p><p><strong>blink</strong>:闪烁</p><p><strong>reverse</strong>:反转</p><p><strong>italic</strong>:斜体</p><h2 id="将本地分支推到远程仓库"><a href="#将本地分支推到远程仓库" class="headerlink" title="将本地分支推到远程仓库"></a>将本地分支推到远程仓库</h2><h3 id="深入解析-git-remote-add-origin"><a href="#深入解析-git-remote-add-origin" class="headerlink" title="深入解析 git remote add origin"></a>深入解析 git remote add origin</h3><p>进入要上传的仓库,右键git bash,添加远程地址:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git remote add origin [email protected]:yourName/yourRepo.git</span></span><br></pre></td></tr></table></figure><blockquote><p>别小看这句经常使用的命令,里面可大有门道</p><p>之后常要用到<strong>origin</strong>,那它到底是什么?</p></blockquote><p>可以通过👇查看</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git remote -v</span></span><br></pre></td></tr></table></figure><p><code>origin</code> 是 Git 中的一个默认命名惯例,通常用于指代克隆的远程仓库。它是一个远程仓库的别名,用于简化和方便地引用和操作远程仓库。</p><p>1.<strong>默认远程仓库</strong>: 当你使用 <code>git clone</code> 命令克隆一个远程仓库时,Git 会自动将这个远程仓库命名为 <code>origin</code>。例如:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/username/repository.git</span><br></pre></td></tr></table></figure><p>这会创建一个名为 <code>origin</code> 的远程指针,指向克隆的仓库。</p><p>2.<strong>远程仓库的别名</strong>: <code>origin</code> 是一个指向远程仓库 URL 的别名。你可以使用 <code>origin</code> 来代替实际的远程仓库 URL,简化命令操作。</p><p>也就是说我们其实可以将本地仓库和多个远程仓库建立联系</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">git remote add origin2 [email protected]:yourName/yourRepo.git</span></span><br></pre></td></tr></table></figure><p>即将另一个远程仓库的ssh记为<strong>origin2</strong></p><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240614195740221.png" alt="image-20240614195740221"></p><h4 id="相关操作👇"><a href="#相关操作👇" class="headerlink" title="相关操作👇"></a>相关操作👇</h4><p><strong>添加新的远程仓库</strong>:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote add new-origin https://github.com/another-user/another-repository.git</span><br></pre></td></tr></table></figure><p><strong>修改现有远程仓库 URL</strong>:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote set-url origin https://github.com/username/new-repository.git</span><br></pre></td></tr></table></figure><p><strong>删除远程仓库</strong>:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote remove origin</span><br></pre></td></tr></table></figure><h3 id="深入解析git-push"><a href="#深入解析git-push" class="headerlink" title="深入解析git push"></a>深入解析git push</h3><p>然后就是git正常操作了</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git add .</span><br><span class="line">git commit -m "abab"</span><br><span class="line">git push</span><br></pre></td></tr></table></figure><p>真就这么简单吗?</p><p><em><strong>nonono,too young too simple</strong></em>,必须得好好讲讲<strong>git push</strong></p><blockquote><p>git push</p><p>其实是一种缩写</p><p>如果你当前在 <code>main</code> 分支上,并且该分支已经设置了仅有的远程分支,才可以直接使用 <code>git push</code> 命令来推送本地的 <code>main</code> 分支到远程仓库</p><p>所以当分支关联了多个远程仓库就不可以这么简单粗暴了</p></blockquote><h4 id="1-push到远程仓库的同名分支中(若无则建立)"><a href="#1-push到远程仓库的同名分支中(若无则建立)" class="headerlink" title="1.push到远程仓库的同名分支中(若无则建立)"></a>1.push到远程仓库的同名分支中(若无则建立)</h4><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push <remote></span><br></pre></td></tr></table></figure><p>当然记得要<code>git checkout <branch></code>切换到要推送的分支上</p><h4 id="✨2-push到远程仓库的其他远程分支中"><a href="#✨2-push到远程仓库的其他远程分支中" class="headerlink" title="✨2.push到远程仓库的其他远程分支中"></a>✨2.push到远程仓库的其他远程分支中</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push <remote> <local-branch>:<remote-branch></span><br></pre></td></tr></table></figure><blockquote><p>这个命令的语法是 <code>git push <remote> <local-branch>:<remote-branch></code>。它的作用是将本地的 <code>local-branch</code> 分支推送到远程仓库<code>remote</code>的 <code>remote-branch</code> 分支。</p></blockquote><h4 id="相关操作👇-1"><a href="#相关操作👇-1" class="headerlink" title="相关操作👇"></a>相关操作👇</h4><p><strong>删除远程分支</strong>:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push <remote-name> --delete <branch-name></span><br></pre></td></tr></table></figure><h3 id="补充下git-branch相关命令"><a href="#补充下git-branch相关命令" class="headerlink" title="补充下git branch相关命令"></a>补充下git branch相关命令</h3><p>这个其是就是一些补充操作</p><ul><li><p><code>git branch</code>:查看本地分支</p></li><li><p><code>git branch -r</code>:查看远程分支</p></li><li><p><code>git branch -a</code>:查看所有分支(本地和远程)</p></li><li><p><code>git branch -v</code>:显示分支详细信息</p></li><li><p><code>git branch <branch-name></code>:创建新分支</p></li><li><p><code>git checkout -b <branch-name></code>:创建并切换到新分支</p></li><li><p><code>git branch -d <branch-name></code>:删除本地分支</p></li><li><p><code>git branch -D <branch-name></code>:强制删除本地分支</p></li><li><p><code>git branch -m <new-branch-name></code>:重命名当前分支</p></li><li><p><code>git branch -m <old-branch-name> <new-branch-name></code>:重命名指定分支</p></li><li><p><code>git branch --set-upstream-to=<remote>/<branch> <local-branch></code>:设置上游分支</p></li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>只有熟练了<code>git branch</code> 、<code>git remote </code>、 <code>git push</code>三类指令才能玩转<strong>本地仓库/分支</strong>和<strong>远程仓库/分支</strong></p><p>当然,在<strong>push or merge</strong>时候还会碰到个头疼问题,就是<strong>conflict</strong>冲突问题,有机会再说喽😁</p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>设计模式DesignPattern【2】--FactoryMethod</title>
<link href="/tangBlog/2024/06/14/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8FDesignPatterns%E3%80%902%E3%80%91-FactoryMethod/"/>
<url>/tangBlog/2024/06/14/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8FDesignPatterns%E3%80%902%E3%80%91-FactoryMethod/</url>
<content type="html"><)</li><li><a href="https://refactoring.guru/design-patterns/factory-method">Factory Method (refactoring.guru)</a>)</li><li><a href="https://github.com/kamranahmedse/design-patterns-for-humans?tab=readme-ov-file#-simple-factory">kamranahmedse/design-patterns-for-humans: An ultra-simplified explanation to design patterns (github.com)</a></li><li><a href="https://www.runoob.com/design-pattern/factory-pattern.html">工厂模式 | 菜鸟教程 (runoob.com)</a></li></ul><h2 id="简单工厂"><a href="#简单工厂" class="headerlink" title="简单工厂"></a>简单工厂</h2><h2 id="工厂方法"><a href="#工厂方法" class="headerlink" title="工厂方法"></a>工厂方法</h2><p>工厂方法模式将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,<strong>扩展时不必去修改原来的代码</strong>。</p><p>在使用时,用于只需知道产品对应的具体工厂,关注具体的创建过程,甚至不需要知道具体产品类的类名,当我们选择哪个具体工厂时,就已经决定了实际创建的产品是哪个了。</p>]]></content>
</entry>
<entry>
<title>设计模式DesignPatterns</title>
<link href="/tangBlog/2024/05/31/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8FDesignPatterns/"/>
<url>/tangBlog/2024/05/31/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8FDesignPatterns/</url>
<content type="html"><</a>)</li><li>[维基百科](<a href="https://en.wikipedia.org/wiki/Software_design_pattern">软件设计模式 - 维基百科,自由的百科全书 — Software design pattern - Wikipedia</a>)</li><li>⭐[**Refactoring.Guru**](<a href="https://refactoringguru.cn/design-patterns">常用设计模式有哪些? (refactoringguru.cn)</a>)</li><li>[**github**](<a href="https://github.com/kamranahmedse/design-patterns-for-humans">kamranahmedse/design-patterns-for-humans: An ultra-simplified explanation to design patterns (github.com)</a>)</li></ul><h2 id="学习笔记"><a href="#学习笔记" class="headerlink" title="学习笔记"></a>学习笔记</h2><p><em><strong>If all you have is a hammer, everything looks like a nail.</strong></em><br>如果你只有一把锤子,那么一切看起来都像钉子。</p><p>这是我看到的对于<strong>学习设计模式利弊</strong>的最好评语</p><p>学习设计模式的好处在于给于我们启示,以一种更加系统结构化的眼光看待问题,并在某种程度上有利于规避潜在问题的出现,但在另一反面一味的套用设计模式也僵化我们的思维,导致遇到什么问题都想机械的套用模板,哪怕是特别简单的问题,这反而会不利于项目的实现。</p><h3 id="什么是设计模式"><a href="#什么是设计模式" class="headerlink" title="什么是设计模式"></a>什么是设计模式</h3><p>Design patterns are solutions to recurring problems; <strong>guidelines on how to tackle certain problems</strong>. They are not classes, packages or libraries that you can plug into your application and wait for the magic to happen. These are, rather, guidelines on how to tackle certain problems in certain situations.</p><blockquote><p>设计模式是反复出现的问题的解决方案;关于如何解决某些问题的指南。它们不是可以插入应用程序并等待奇迹发生的类、包或库。相反,这些是关于如何在某些情况下解决某些问题的指导方针。</p></blockquote><p>Design patterns are solutions to recurring problems; guidelines on how to tackle certain problems</p><blockquote><p>设计模式是反复出现的问题的解决方案;关于如何解决某些问题的指南</p></blockquote><p><strong>Design patterns</strong> are typical solutions to commonly occurring problems in software design. They are like pre-made blueprints that you can customize to solve a recurring design problem in your code.</p><blockquote><p>设计模式是软件设计中常见问题的典型解决方案。它们就像预制的蓝图,您可以对其进行自定义以解决代码中反复出现的设计问题。</p></blockquote><h4 id="与算法的区别"><a href="#与算法的区别" class="headerlink" title="与算法的区别"></a>与算法的区别</h4><p>Patterns are often confused with algorithms, because both concepts describe typical solutions to some known problems. While an algorithm always defines a clear set of actions that can achieve some goal, a pattern is a more high-level description of a solution. The code of the same pattern applied to two different programs may be different.</p><blockquote><p>模式经常与算法混淆,因为这两个概念都描述了一些已知问题的典型解决方案。虽然算法总是定义一组可以实现某些目标的明确操作,但模式是对解决方案的更高级描述。应用于两个不同程序的相同模式的代码可能不同。</p></blockquote><p>An analogy to an algorithm is a cooking recipe: both have clear steps to achieve a goal. On the other hand, a pattern is more like a blueprint: you can see what the result and its features are, but the exact order of implementation is up to you.</p><blockquote><p>算法的类比是烹饪食谱:两者都有明确的步骤来实现目标。另一方面,模式更像是蓝图:你可以看到结果和它的功能是什么,但确切的实现顺序取决于你。</p></blockquote><h4 id="设计模式的主要元素"><a href="#设计模式的主要元素" class="headerlink" title="设计模式的主要元素"></a>设计模式的主要元素</h4><ul><li><p><strong>Intent</strong> of the pattern briefly describes both the problem and the solution.</p><blockquote><p>模式的<strong>意图</strong>简要描述了问题和解决方案。</p></blockquote></li><li><p><strong>Motivation</strong> further explains the problem and the solution the pattern makes possible.</p><blockquote><p><strong>动机</strong>进一步解释了问题和模式使解决方案成为可能。</p></blockquote></li><li><p><strong>Structure</strong> of classes shows each part of the pattern and how they are related.</p><blockquote><p>类的<strong>结构</strong>显示了模式的每个部分以及它们之间的关系。</p></blockquote></li><li><p><strong>Code example</strong> in one of the popular programming languages makes it easier to grasp the idea behind the pattern.</p><blockquote><p>一种流行的编程语言的<strong>代码示例</strong>可以更轻松地掌握模式背后的思想。</p></blockquote></li></ul><h4 id="设计模式的六大原则"><a href="#设计模式的六大原则" class="headerlink" title="设计模式的六大原则"></a>设计模式的六大原则</h4><p><strong>1、开闭原则(Open Close Principle)</strong></p><p>开闭原则的意思是:<strong>对扩展开放,对修改关闭</strong>。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。</p><p><strong>2、里氏代换原则(Liskov Substitution Principle)</strong></p><p>里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。</p><p><strong>3、依赖倒转原则(Dependence Inversion Principle)</strong></p><p>这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。</p><p><strong>4、接口隔离原则(Interface Segregation Principle)</strong></p><p>这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。</p><p><strong>5、迪米特法则,又称最少知道原则(Demeter Principle)</strong></p><p>最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。</p><p><strong>6、合成复用原则(Composite Reuse Principle)</strong></p><p>合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。</p><h3 id="history-of-patterns"><a href="#history-of-patterns" class="headerlink" title="history of patterns"></a>history of patterns</h3><p> 设计模式的发明很难确定于某一具体的人,通常是在代码实践过程对于某类项目一次又一次的反复出现的解决方法,人们最终将其记录命名记录下来。</p><p>Who invented patterns? That’s a good, but not a very accurate, question. Design patterns aren’t obscure, sophisticated concepts—quite the opposite. Patterns are typical solutions to common problems in object-oriented design. When a solution gets repeated over and over in various projects, someone eventually puts a name to it and describes the solution in detail. That’s basically how a pattern gets discovered.</p><blockquote><p>谁发明了图案?这是一个很好的问题,但不是一个非常准确的问题。设计模式并不是晦涩难懂的复杂概念,恰恰相反。模式是面向对象设计中常见问题的典型解决方案。当一个解决方案在各种项目中一遍又一遍地重复时,最终会有人给它起一个名字并详细描述该解决方案。这基本上就是模式被发现的方式。</p></blockquote><p>The concept of patterns was first described by Christopher Alexander in <a href="https://refactoring.guru/pattern-language-book">A Pattern Language: Towns, Buildings, Construction</a>. The book describes a “language” for designing the urban environment. The units of this language are patterns. They may describe how high windows should be, how many levels a building should have, how large green areas in a neighborhood are supposed to be, and so on.</p><blockquote><p>模式的概念最早是由克里斯托弗·亚历山大(Christopher Alexander)在《模式语言:城镇、建筑物、建筑》一书中描述的。该书描述了一种设计城市环境的“语言”。这种语言的单位是模式。他们可能会描述窗户应该有多高,建筑物应该有多少层,社区中的绿地应该有多大,等等。</p></blockquote><p>The idea was picked up by four authors: Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm. In 1994, they published <a href="https://refactoring.guru/gof-book">Design Patterns: Elements of Reusable Object-Oriented Software</a>, in which they applied the concept of design patterns to programming. The book featured 23 patterns solving various problems of object-oriented design and became a best-seller very quickly. Due to its lengthy name, people started to call it “the book by the gang of four” which was soon shortened to simply “the GoF book”.</p><blockquote><p>这个想法被四位作者采纳:Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm。1994 年,他们出版了《设计模式:可重用面向对象软件的元素》,其中他们将设计模式的概念应用于编程。这本书收录了 23 种模式,解决了面向对象设计的各种问题,并很快成为畅销书。由于它的名字很长,人们开始称它为“四人帮的书”,很快就被简称为“GoF书”。</p></blockquote><p>Since then, dozens of other object-oriented patterns have been discovered. The “pattern approach” became very popular in other programming fields, so lots of other patterns now exist outside of object-oriented design as well.</p><blockquote><p>从那时起,又发现了数十种其他面向对象的模式。“模式方法”在其他编程领域变得非常流行,因此现在在面向对象设计之外也存在许多其他模式。</p></blockquote><h3 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h3><ul><li><p><strong>Creational patterns</strong> provide object creation mechanisms that increase flexibility and reuse of existing code.</p><blockquote><p>创建模式提供了对象创建机制,这些机制可提高现有代码的灵活性和重用性。</p></blockquote></li><li><p><strong>Structural patterns</strong> explain how to assemble objects and classes into larger structures, while keeping these structures flexible and efficient.</p><blockquote><p>结构模式解释了如何将对象和类组装成更大的结构,同时保持这些结构的灵活性和效率。</p></blockquote></li><li><p><strong>Behavioral patterns</strong> take care of effective communication and the assignment of responsibilities between objects.</p><blockquote><p>行为模式负责对象之间的有效沟通和责任分配。</p></blockquote></li></ul><table><thead><tr><th align="left">序号</th><th align="left">模式 & 描述</th><th align="left">包括</th></tr></thead><tbody><tr><td align="left">1</td><td align="left"><strong>创建型模式</strong> 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,<br />而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。</td><td align="left">工厂模式(Factory Pattern) <br />抽象工厂模式(Abstract Factory Pattern)<br />单例模式(Singleton Pattern)<br />建造者模式(Builder Pattern)<br />原型模式(Prototype Pattern)</td></tr><tr><td align="left">2</td><td align="left"><strong>结构型模式</strong> 这些模式关注对象之间的组合和关系,<br />旨在解决如何构建灵活且可复用的类和对象结构。</td><td align="left">适配器模式(Adapter Pattern)<br />桥接模式(Bridge Pattern)<br />过滤器模式(Filter、Criteria Pattern)<br />组合模式(Composite Pattern)<br />装饰器模式(Decorator Pattern)<br />外观模式(Facade Pattern)<br />享元模式(Flyweight Pattern)<br />代理模式(Proxy Pattern)</td></tr><tr><td align="left">3</td><td align="left"><strong>行为型模式</strong> 这些模式关注对象之间的通信和交互,<br />旨在解决对象之间的责任分配和算法的封装。</td><td align="left">责任链模式(Chain of Responsibility Pattern)<br />命令模式(Command Pattern)<br />解释器模式(Interpreter Pattern)<br />迭代器模式(Iterator Pattern)<br />中介者模式(Mediator Pattern)<br />备忘录模式(Memento Pattern)<br />观察者模式(Observer Pattern)<br />状态模式(State Pattern)<br />空对象模式(Null Object Pattern)<br />策略模式(Strategy Pattern)<br />模板模式(Template Pattern)<br />访问者模式(Visitor Pattern)</td></tr><tr><td align="left">4</td><td align="left"><strong>J2EE 模式</strong> 这些设计模式特别关注表示层。<br />这些模式是由 Sun Java Center 鉴定的。</td><td align="left">MVC 模式(MVC Pattern)<br />业务代表模式(Business Delegate Pattern)<br />组合实体模式(Composite Entity Pattern)数据访问对象模式(Data Access Object Pattern)<br />前端控制器模式(Front Controller Pattern)<br />拦截过滤器模式(Intercepting Filter Pattern)<br />服务定位器模式(Service Locator Pattern)<br />传输对象模式(Transfer Object Pattern)</td></tr></tbody></table>]]></content>
</entry>
<entry>
<title>java语法回眸</title>
<link href="/tangBlog/2024/05/21/java%E8%AF%AD%E6%B3%95%E5%9B%9E%E7%9C%B8/"/>
<url>/tangBlog/2024/05/21/java%E8%AF%AD%E6%B3%95%E5%9B%9E%E7%9C%B8/</url>
<content type="html"><</a>)</p><h3 id="Character-String-StringBuffer类"><a href="#Character-String-StringBuffer类" class="headerlink" title="Character & String & StringBuffer类"></a>Character & String & StringBuffer类</h3><p>[具体函数自己看](<a href="https://www.runoob.com/java/java-string.html">Java String 类 | 菜鸟教程 (runoob.com)</a>)</p><ul><li><p><a href="https://www.runoob.com/java/java-string-concat.html"> String concat(String str)</a> 将指定字符串连接到此字符串的结尾。</p></li><li><p><a href="https://www.runoob.com/java/java-string-length.html"> int length()</a> 返回此字符串的长度。</p></li><li><p><a href="https://www.runoob.com/java/java-string-trim.html"> String trim()</a> 返回字符串的副本,忽略前导空白和尾部空白。</p></li></ul><h2 id="最重要的OOP思想"><a href="#最重要的OOP思想" class="headerlink" title="最重要的OOP思想"></a>最重要的OOP思想</h2><ul><li><p>类(class)</p></li><li><p>对象(object)</p></li><li><p>实例(instance)</p></li><li><p>多态</p></li><li><p>继承(inheritance)</p></li><li><p>封装(encapsulation)</p></li><li><p>方法</p></li><li><p>抽象类</p></li><li><p>重载</p></li><li><p>重写</p></li></ul><h3 id="对象是类的实例,类是对象的抽象"><a href="#对象是类的实例,类是对象的抽象" class="headerlink" title="对象是类的实例,类是对象的抽象"></a>对象是类的实例,类是对象的抽象</h3><p>构造器(constructor)用来创造实例</p><p>访问器 getter setter 实现封装</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Achievement</span> <span class="keyword">extends</span> <span class="title class_">BaseActivity</span> {</span><br><span class="line"></span><br><span class="line"><span class="comment">//instance fields</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> achievementId;</span><br><span class="line"> <span class="keyword">private</span> String title;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">boolean</span> isfinished;</span><br><span class="line"></span><br><span class="line"><span class="comment">//constructor</span></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Achievement</span><span class="params">(<span class="type">int</span> achievementId, String title, <span class="type">boolean</span> isfinished)</span> {</span><br><span class="line"> <span class="built_in">this</span>.achievementId = achievementId;</span><br><span class="line"> <span class="built_in">this</span>.title = title;</span><br><span class="line"> <span class="built_in">this</span>.isfinished = isfinished;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"><span class="comment">//method</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAchievementId</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> achievementId+<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getTitle</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> title;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isIsfinished</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> isfinished;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setIsfinished</span><span class="params">(<span class="type">boolean</span> isfinished)</span> {</span><br><span class="line"> <span class="built_in">this</span>.isfinished = isfinished;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id=""><a href="#" class="headerlink" title=""></a></h2><h3 id="继承(inheritance)"><a href="#继承(inheritance)" class="headerlink" title="继承(inheritance)"></a>继承(inheritance)</h3><h4 id="extends关键字"><a href="#extends关键字" class="headerlink" title="extends关键字"></a>extends关键字</h4><p>在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。</p><h4 id="implements关键字"><a href="#implements关键字" class="headerlink" title="implements关键字"></a>implements关键字</h4><p>使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">A</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sleep</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">B</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">show</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">C</span> <span class="keyword">implements</span> <span class="title class_">A</span>,B {</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="重写-Override-与重载-Overload"><a href="#重写-Override-与重载-Overload" class="headerlink" title="重写(Override)与重载(Overload)"></a>重写(Override)与重载(Overload)</h3><ul><li><p><strong>重写(Override)</strong>是指子类定义了一个与其父类中具有相同名称、参数列表和返回类型的方法,并且子类方法的实现覆盖了父类方法的实现。 <strong>即外壳不变,核心重写!</strong></p><p>当需要在子类中调用父类的被重写方法时,要使用 <strong>super</strong> 关键字。</p></li><li><p><strong>重载(overloading)</strong> 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。</p></li><li><p><strong>方法重载</strong>是一个类的多态性表现,而<strong>方法重写</strong>是子类与父类的一种多态性表现</p></li></ul><p>重写是将父类的方法逻辑重写(儿子叛逆但未变异)</p><p>重载是使同名方法有不同逻辑</p><h4 id="方法的重写规则"><a href="#方法的重写规则" class="headerlink" title="方法的重写规则"></a>方法的重写规则</h4><ul><li>参数列表与被重写方法的参数列表必须完全相同。</li><li>返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。</li><li>访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。</li><li>父类的成员方法只能被它的子类重写。</li><li>声明为 final 的方法不能被重写。</li><li>声明为 static 的方法不能被重写,但是能够被再次声明。</li><li>子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。</li><li>子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。</li><li>重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。</li><li>构造方法不能被重写。</li><li>如果不能继承一个类,则不能重写该类的方法。</li></ul><h4 id="重载规则"><a href="#重载规则" class="headerlink" title="重载规则"></a><strong>重载规则</strong></h4><ul><li>被重载的方法必须改变参数列表(参数个数或类型不一样);</li><li>被重载的方法可以改变返回类型;</li><li>被重载的方法可以改变访问修饰符;</li><li>被重载的方法可以声明新的或更广的检查异常;</li><li>方法能够在同一个类中或者在一个子类中被重载。</li><li>无法以返回值类型作为重载函数的区分标准。</li></ul><h4 id="重写与重载之间的区别"><a href="#重写与重载之间的区别" class="headerlink" title="重写与重载之间的区别"></a>重写与重载之间的区别</h4><table><thead><tr><th align="left">区别点</th><th align="left">重载方法</th><th align="left">重写方法</th></tr></thead><tbody><tr><td align="left">参数列表</td><td align="left">必须修改</td><td align="left">一定不能修改</td></tr><tr><td align="left">返回类型</td><td align="left">可以修改</td><td align="left">一定不能修改</td></tr><tr><td align="left">异常</td><td align="left">可以修改</td><td align="left">可以减少或删除,一定不能抛出新的或者更广的异常</td></tr><tr><td align="left">访问</td><td align="left">可以修改</td><td align="left">一定不能做更严格的限制(可以降低限制)</td></tr></tbody></table><h3 id="抽象类"><a href="#抽象类" class="headerlink" title="抽象类"></a>抽象类</h3><ul><li>抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类</li><li>抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类</li></ul><h3 id="枚举类(enum)"><a href="#枚举类(enum)" class="headerlink" title="枚举类(enum)"></a>枚举类(enum)</h3><h3 id="内部类"><a href="#内部类" class="headerlink" title="内部类"></a>内部类</h3><p>内部类是定义在另一个类内部的类</p><h3 id="内部类的类型"><a href="#内部类的类型" class="headerlink" title="内部类的类型"></a>内部类的类型</h3><p>写着就明白了(帮忙简化代码的)</p><ol><li><strong>成员内部类</strong>(Member Inner Class)</li><li><strong>局部内部类</strong>(Local Inner Class)</li><li><strong>匿名内部类</strong>(Anonymous Inner Class)</li><li><strong>静态内部类</strong>(Static Nested Class)</li></ol><p>成员内部类定义在另一个类的内部,并且作为外部类的一个成员存在。它可以访问外部类的所有成员,包括私有成员。</p><p>局部内部类定义在方法或作用域块中。它只能在该方法或作用域块内使用。</p><p>匿名内部类是一种没有名字的内部类,通常用来简化代码,尤其是用于实现接口或抽象类的实例。</p><p>静态内部类使用 <code>static</code> 修饰,独立于外部类的实例。它只能访问外部类的静态成员。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HomePage</span> <span class="keyword">extends</span> <span class="title class_">BaseActivity</span> { </span><br><span class="line"> <span class="comment">//内部类</span></span><br><span class="line"> <span class="keyword">private</span> ImageButton btnZhaomu;</span><br><span class="line"> btnZhaomu.setOnClickListener(<span class="keyword">new</span> <span class="title class_">View</span>.OnClickListener() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onClick</span><span class="params">(View v)</span> {</span><br><span class="line"> <span class="type">Intent</span> <span class="variable">intent</span> <span class="operator">=</span> Achievement_Activity.newIntent(HomePage.<span class="built_in">this</span>);</span><br><span class="line"> startActivity(intent);</span><br><span class="line"> finish();</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>Git基础</title>
<link href="/tangBlog/2024/05/03/Git%E5%9F%BA%E7%A1%80/"/>
<url>/tangBlog/2024/05/03/Git%E5%9F%BA%E7%A1%80/</url>
<content type="html"><![CDATA[<h1 id="Git"><a href="#Git" class="headerlink" title="Git"></a>Git</h1><h2 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h2><p>需要会点linux和shell才能方便进行一些基本操作</p><p>linux</p><ul><li>ls(英文全拼:list files): 列出目录及文件名</li><li>cd(英文全拼:change directory):切换目录</li><li>pwd(英文全拼:print work directory):显示目前的目录</li><li>mkdir(英文全拼:make directory):创建一个新的目录</li><li>rmdir(英文全拼:remove directory):删除一个空的目录</li><li>cp(英文全拼:copy file): 复制文件或目录</li><li>rm(英文全拼:remove): 删除文件或目录</li><li>mv(英文全拼:move file): 移动文件与目录,或修改文件与目录的名称</li></ul><p>shell</p><ul><li><strong>i</strong> – 切换到输入模式,在光标当前位置开始输入文本。</li><li><strong>x</strong> – 删除当前光标所在处的字符。</li><li><strong>:</strong> – 切换到底线命令模式,以在最底一行输入命令。</li><li><strong>a</strong> – 进入插入模式,在光标下一个位置开始输入文本。</li><li><strong>o</strong>:在当前行的下方插入一个新行,并进入插入模式。</li><li><strong>O</strong> – 在当前行的上方插入一个新行,并进入插入模式。</li><li><strong>dd</strong> – 剪切当前行。</li><li><strong>yy</strong> – 复制当前行。</li><li><strong>p</strong>(小写) – 粘贴剪贴板内容到光标下方。</li><li><strong>P</strong>(大写)– 粘贴剪贴板内容到光标上方。</li><li><strong>u</strong> – 撤销上一次操作。</li><li><strong>Ctrl + r</strong> – 重做上一次撤销的操作。</li><li><strong>:w</strong> – 保存文件。</li><li><strong>:q</strong> – 退出 Vim 编辑器。</li><li><strong>:q!</strong> – 强制退出Vim 编辑器,不保存修改。</li></ul><h2 id="初始化配置"><a href="#初始化配置" class="headerlink" title="初始化配置"></a>初始化配置</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><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">git -v </span><br><span class="line"></span><br><span class="line">git config --global user.name "<username>"</span><br><span class="line"></span><br><span class="line">git config --global user.email <email></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">git环境配置信息</span></span><br><span class="line">git config --global --list</span><br></pre></td></tr></table></figure><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427132446217.png" alt="image-20240427132446217"></p><h2 id="创建仓库"><a href="#创建仓库" class="headerlink" title="创建仓库"></a>创建仓库</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">create a new <span class="built_in">dir</span> to store the gitreso</span></span><br><span class="line">mkdir <dirname></span><br><span class="line">git init</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">look all resouce</span></span><br><span class="line">ls -a</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">create a new git reso <span class="keyword">in</span> the present <span class="built_in">dir</span></span></span><br><span class="line">git init <newdirname></span><br></pre></td></tr></table></figure><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427133258830.png" alt="image-20240427133258830"></p><h2 id="git-workspace-and-file-status"><a href="#git-workspace-and-file-status" class="headerlink" title="git workspace and file status"></a>git workspace and file status</h2><p>git workspace</p><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427134557468.png" alt="image-20240427134557468">file status</p><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427135209734.png" alt="image-20240427135209734"></p><p>untrack *the new created file</p><p>unmodified *the unchanged git file</p><p>modified *the changed git file but haven’t add into the Staging Area</p><p>staged *the changed git file and already add into the SA</p><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427135733187.png" alt="image-20240427135733187"></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">present gitdir status</span></span><br><span class="line">git status</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">add file to AS</span></span><br><span class="line">git add <filename></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">add present <span class="built_in">dir</span><span class="string">'s all changed file to AS</span></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">'</span>.<span class="string">' means to present dir</span></span></span><br><span class="line">git add .</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">add all the type of files is .txt to AS</span></span></span><br><span class="line">git add *.txt</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">commit the files <span class="keyword">in</span> AS</span></span><br><span class="line">git commit -m "<title>"</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">into Vim mode to commit files <span class="keyword">in</span> AS</span></span><br><span class="line">git commit </span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">finish add and commit at onec</span></span><br><span class="line">git commit -am ""</span><br></pre></td></tr></table></figure><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">search past commit record</span></span><br><span class="line">git log</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">brief commit record</span></span><br><span class="line">git log --oneline</span><br></pre></td></tr></table></figure><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427195749947.png" alt="image-20240427195749947"></p><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427195931931.png" alt="image-20240427195931931"></p><h2 id="git-reset"><a href="#git-reset" class="headerlink" title="git reset"></a>git reset</h2><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427200045013.png" alt="image-20240427200045013"></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">git log --online</span><br><span class="line"></span><br><span class="line">git reset --soft <commit_hash></span><br><span class="line">git reset --hard <commit_hash></span><br><span class="line">git reset --mixed <commit_hash></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">roolback to last version</span></span><br><span class="line">git reset --sofe HEAD^</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">roolback roolback to the last version</span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># to get the version number</span></span></span><br><span class="line">git reflog</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># roolback roolback to the last version</span></span></span><br><span class="line">gir reset --hard <commit_hash></span><br></pre></td></tr></table></figure><h2 id="git-diff"><a href="#git-diff" class="headerlink" title="git diff"></a>git diff</h2><p>usually use the GUI tools</p><p>but sometimes we need to learn about it to run some machine which without GUI tools </p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">compare diffrences between the Working directory and Staging area</span></span><br><span class="line">git diff</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">compare diffrences between the Working directory and <span class="built_in">local</span> repository</span></span><br><span class="line">git diff HEAD</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">compare diffrences between the Staging area and <span class="built_in">local</span> repository</span></span><br><span class="line">git diff --cached</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">compare diffrences between two version file</span></span><br><span class="line">git diff <commit_hash1> <commit_hash2></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment"># two symbol</span></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">'HEAD'</span> means present version</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">'~'</span> or <span class="string">'^'</span> means last version</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">so we can <span class="keyword">do</span> like this to</span> </span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">#</span></span></span><br><span class="line">git diff HEAD~ HEAD</span><br><span class="line">git diff HEAD^ HEAD</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">and we can input a number behind the ~ <span class="built_in">which</span> means last N verision</span></span><br><span class="line">git diff HEAD~2 HEAD</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">check specific files diff</span></span><br><span class="line">git diff HEAD~2 HEAD <filename></span><br></pre></td></tr></table></figure><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427202747326.png" alt="image-20240427202747326"></p><h2 id="rm-files"><a href="#rm-files" class="headerlink" title="rm files"></a>rm files</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">just remove the files <span class="keyword">in</span> Working dirctory but doesn<span class="string">'s remove in AS</span></span> </span><br><span class="line">rm <filename></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="string">so we need git add . to update the diff into AS</span></span></span><br><span class="line">git add .</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">or we can rm directly by use git rm</span></span></span><br><span class="line">git rm <filename></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"><span class="string">rm files in local repository but don'</span>t <span class="built_in">rm</span> <span class="keyword">in</span> WD</span></span><br><span class="line">git rm --cached <filename></span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">at last we need to commit to updata diff into <span class="built_in">local</span> repository</span></span><br><span class="line">git commit -m ""</span><br></pre></td></tr></table></figure><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427204153159.png" alt="image-20240427204153159"></p><h2 id="gitignore"><a href="#gitignore" class="headerlink" title=".gitignore"></a>.gitignore</h2><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427204329307.png" alt="image-20240427204329307"></p><p>we can search template in github</p><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427205948513.png" alt="image-20240427205948513"></p><h2 id="clone-remote-Repo"><a href="#clone-remote-Repo" class="headerlink" title="clone remote Repo"></a>clone remote Repo</h2><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427210451419.png" alt="image-20240427210451419"></p><p><img src="C:\Users\21418\AppData\Roaming\Typora\typora-user-images\image-20240427210545105.png" alt="image-20240427210545105"></p>]]></content>
<tags>
<tag> Git </tag>
</tags>
</entry>
<entry>
<title>ROS</title>
<link href="/tangBlog/2024/04/22/ROS/"/>
<url>/tangBlog/2024/04/22/ROS/</url>
<content type="html"><![CDATA[<h1 id="ROS"><a href="#ROS" class="headerlink" title="ROS"></a>ROS</h1><h2 id="Ros是什么"><a href="#Ros是什么" class="headerlink" title="Ros是什么"></a>Ros是什么</h2><p>模块化、分布式</p><p>ROS: An Open-Source Robot Operating System</p><p>机器人界的Android</p><h2 id="Ubuntu-入门"><a href="#Ubuntu-入门" class="headerlink" title="Ubuntu 入门"></a>Ubuntu 入门</h2><p>中英文切换 shift win+space</p><p>系统文件</p><p>连接网络 右上角</p><p>终端程序 CTRL+L+T</p><ol><li>ls list 罗列清单</li><li>mkdir make directory 创建新的目录</li><li>cd change directory 进入目录</li><li>cd .. 回到上级目录</li><li>cd~ 回到主文件夹</li><li>Tap 自动补全指令或目录</li></ol><p>gedit 编辑 文本编辑器类似txt</p><p>source 执行文件</p><p>终端启动脚本 .bashrc</p><p>sudo 管理员执行</p><h2 id="github资源下载"><a href="#github资源下载" class="headerlink" title="github资源下载"></a>github资源下载</h2><p>scripts 目录用于放置脚本文件(安装依赖包等低频率的操作)和python程序</p><h3 id="资源寻找"><a href="#资源寻找" class="headerlink" title="资源寻找"></a>资源寻找</h3><ol><li>Index.ros上搜索</li><li>GitHub上搜索</li></ol><h3 id="资源下载"><a href="#资源下载" class="headerlink" title="资源下载"></a>资源下载</h3><p>cd catkin_ws/src/ 进入src文件</p><p>git clone https….. 下载资源到src上</p><p>cd ~/catkin_ws/ 进入catkin_ws目录</p><p>catkin_make 编译</p><p>Terminator 超级终端 Ctrl+Alt+T</p><ol><li><p>ctrl +shift +E 左右分屏终端</p></li><li><p>ctrl +shift+O 上下分屏终端</p></li><li><p>ALT + ←方向键 切换终端操作焦点</p></li><li><p>ctrl +shift +W 关闭当前终端</p></li></ol><h2 id="Node-package"><a href="#Node-package" class="headerlink" title="Node & package"></a>Node & package</h2><p>vscode 快捷键</p><ol><li>ctrl + shift + B 编译</li></ol><p>基本流程</p><ol><li>使用catkin_create_pkg创建一个软件包</li><li>在软件包的src文件夹下创建一个节点的cpp源码文件</li><li>在节点的源码文件中#include <ros/ros.h></li><li>构建一个main,并在函数的开头执行ros::init()</li><li>构建while循环,while(ros::ok());</li><li>在CMakeLists.txt中设置节点源码的编译规则</li><li>ctrl shift b 编译</li><li>终端 rosrun <packagename> <nodename>运行</li></ol><h2 id="Topic-Message"><a href="#Topic-Message" class="headerlink" title="Topic & Message"></a>Topic & Message</h2><p>发送方publisher 接收方subscriber</p><ol><li>Topic 是nodes间进行<strong>持续通信</strong>的一种形式</li><li>nodes间通过话题名称建立其话题通讯连接</li><li>Message 通讯的数据</li><li>Message 按一定频率持续不断的发送,以保证消息数据的实时性</li></ol>]]></content>
</entry>
<entry>
<title>python数据分析</title>
<link href="/tangBlog/2024/04/12/python%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/"/>
<url>/tangBlog/2024/04/12/python%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90/</url>
<content type="html"><![CDATA[<p># 数据分析(1)</p><p>这篇简单介绍一下数据分析中常用到的几个库函数,强烈推荐使用<strong>JupyterNotebook</strong>,有机会写一篇教程速通一下,下面的图也都是直接从Jupyter中截取出来的。</p><h2 id="基础库介绍"><a href="#基础库介绍" class="headerlink" title="基础库介绍"></a>基础库介绍</h2><table><thead><tr><th>Numpy</th><th>Pandas</th><th>Matplotlib</th></tr></thead><tbody><tr><td>主要用于<strong>处理多维数组和矩阵运算</strong></td><td>用于<strong>数据处理和分析</strong>的库,提供了DataFrame数据结构和各种数据操作功能,如数据清洗、转换、筛选等</td><td>用于<strong>数据可视化</strong>的库,提供了各种绘图函数和工具,可以创建各种类型的图表,如折线图、柱状图、散点图等</td></tr></tbody></table><h3 id="Numpy"><a href="#Numpy" class="headerlink" title="Numpy"></a><a href="https://numpy.org/">Numpy</a></h3><p>简介:NumPy 是 Python 中科学计算的基础包。它是一个 Python 库,提供多维数组对象、各种派生对象(例如掩码数组和矩阵)以及用于对数组进行快速操作的各种例程,包括数学、逻辑、形状操作、排序、选择、I/O、离散傅里叶变换、基本线性代数、基本统计运算、随机模拟等等。</p><p><strong>数组array</strong>是 NumPy 库的中心数据结构。数组是值的网格,它包含有关原始数据、如何定位元素以及如何解释元素的信息。它有一个元素网格,可以以各种方式进行索引。这些元素都属于同一类型,称为数组 dtype 。</p><p><em><strong>人话就是方便进行数组、矩阵运算</strong></em></p><p><a href="https://scipy.org/">Scipy</a></p><p>简介:Scipy是一个基于NumPy的Python科学计算库,提供了更多高级的数学、科学和工程计算功能。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment">#创建初始数组(矩阵)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"1d array"</span>)</span><br><span class="line">a = np.arange(<span class="number">6</span>) <span class="comment"># 1d array</span></span><br><span class="line"><span class="built_in">print</span>(a)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"2d array"</span>)</span><br><span class="line">b = np.arange(<span class="number">12</span>).reshape(<span class="number">4</span>, <span class="number">3</span>) <span class="comment"># 2d array</span></span><br><span class="line"><span class="built_in">print</span>(b)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"3d array"</span>)</span><br><span class="line">c = np.arange(<span class="number">30</span>).reshape(<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>) <span class="comment"># 3d array</span></span><br><span class="line"><span class="built_in">print</span>(c)</span><br><span class="line"></span><br><span class="line">arr = np.random.normal(size=<span class="number">1000</span>)</span><br><span class="line"><span class="built_in">print</span>(arr)</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/bde94e8e101d49bab9136bf332631ba0.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#数组运算 maximum, minimum, sum, mean, product, standard deviation, and more</span></span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">plt.hist(arr)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"max:"</span>,arr.<span class="built_in">max</span>())</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"min:"</span>,arr.<span class="built_in">min</span>())</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"mean:"</span>,arr.mean())</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"sum:"</span>,arr.<span class="built_in">sum</span>())</span><br><span class="line"></span><br><span class="line"><span class="comment"># import matplotlib.pyplot as plt</span></span><br><span class="line"><span class="comment"># plt.hist(arr,bins=15)</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"std:"</span>,arr.std())</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/e6ba736217df40c4b32f8b4c0e7501f7.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#矩阵运算</span></span><br><span class="line">matrix=np.array([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">5</span>, <span class="number">3</span>], [<span class="number">4</span>, <span class="number">6</span>]])</span><br><span class="line"><span class="built_in">print</span>(matrix)</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/b245afa4796b469b9a92d4d5515e3b50.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#按行/列求最大值 列:axis=0,行:axis=1</span></span><br><span class="line">m0=matrix.<span class="built_in">max</span>(axis=<span class="number">0</span>)</span><br><span class="line">m1=matrix.<span class="built_in">max</span>(axis=<span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(m0)</span><br><span class="line"><span class="built_in">print</span>(m1)</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/08615b67de6645d4919e376a72c3bb94.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#reshape重塑矩阵 arr.reshape()</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"reshape"</span>)</span><br><span class="line"><span class="built_in">print</span>(matrix)</span><br><span class="line">rmatrix=matrix.reshape(<span class="number">2</span>,<span class="number">3</span>)</span><br><span class="line"><span class="built_in">print</span>(rmatrix)</span><br><span class="line"></span><br><span class="line"><span class="comment">#transpose 转置 arr.transpose() arr.T</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"transpose"</span>)</span><br><span class="line"><span class="built_in">print</span>(matrix.transpose())</span><br><span class="line"><span class="built_in">print</span>(matrix.T)</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/5d7625b6714d41baa519f51a0e38fee5.png#pic_center" alt="在这里插入图片描述"></p><h3 id="Matplotlib"><a href="#Matplotlib" class="headerlink" title="Matplotlib"></a><a href="https://matplotlib.org/">Matplotlib</a></h3><p><em><strong>人话就是画图的</strong></em></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">x1=np.random.rand(<span class="number">10</span>)</span><br><span class="line">x2=np.random.rand(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">fig = plt.figure()</span><br><span class="line">ax = fig.add_subplot(<span class="number">221</span>) </span><br><span class="line">ax.plot(x1)</span><br><span class="line">ax = fig.add_subplot(<span class="number">222</span>)</span><br><span class="line">ax.plot(x2)</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/22057b4953214adaac6d4b14e14117fb.png#pic_center" alt="在这里插入图片描述"></p><p>你可以一个一个设置基础属性</p><figure class="highlight python"><table><tr><td class="gutter"><pre><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">fig = plt.figure()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Creating subplot/axes</span></span><br><span class="line">ax = fig.add_subplot(<span class="number">111</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Setting plot title</span></span><br><span class="line">ax.set_title(<span class="string">'My plot title'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Setting X-axis and Y-axis limits</span></span><br><span class="line">ax.set_xlim([<span class="number">0</span>, <span class="number">10</span>])</span><br><span class="line">ax.set_ylim([-<span class="number">5</span>, <span class="number">5</span>])</span><br><span class="line"></span><br><span class="line"><span class="comment"># Setting X-axis and Y-axis labels</span></span><br><span class="line">ax.set_ylabel(<span class="string">'My y-axis label'</span>)</span><br><span class="line">ax.set_xlabel(<span class="string">'My x-axis label'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Showing the plot</span></span><br><span class="line">plt.show() </span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/bb5c3514784b44d99054795fe3995679.png#pic_center" alt="在这里插入图片描述"></p><p>也可以一口气设置</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">fig = plt.figure()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Creating subplot/axes</span></span><br><span class="line">ax = fig.add_subplot(<span class="number">111</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Setting title and axes properties</span></span><br><span class="line">ax.<span class="built_in">set</span>(title=<span class="string">'An Axes Title'</span>, xlim=[<span class="number">0</span>, <span class="number">10</span>], ylim=[-<span class="number">5</span>, <span class="number">5</span>], ylabel=<span class="string">'My y-axis label'</span>, xlabel=<span class="string">'My x-axis label'</span>)</span><br><span class="line"></span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/a3bbafbdc78e406c816d304662bc608f.png#pic_center" alt="在这里插入图片描述"></p><p>下面主要以plot为例,matplotlib的<a href="https://matplotlib.org/stable/plot_types/index.html">图像类型</a>其实相当丰富</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">x=np.random.rand(<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot lists 'x' </span></span><br><span class="line">plt.plot(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Plot axes labels and show the plot</span></span><br><span class="line">plt.xlabel(<span class="string">'X-axis Label'</span>)</span><br><span class="line">plt.show() </span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/4efd91dfed2c4959bc298a1b273cb5f7.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Create a figure with four subplots and shared axes</span></span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line">x=np.random.rand(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">fig, axes = plt.subplots(nrows=<span class="number">2</span>, ncols=<span class="number">2</span>, sharex=<span class="literal">True</span>, sharey=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">axes[<span class="number">0</span>, <span class="number">0</span>].<span class="built_in">set</span>(title=<span class="string">'Upper Left'</span>)</span><br><span class="line">axes[<span class="number">0</span>, <span class="number">0</span>].plot(x)</span><br><span class="line"></span><br><span class="line"><span class="comment">##设置颜色</span></span><br><span class="line">axes[<span class="number">0</span>, <span class="number">1</span>].<span class="built_in">set</span>(title=<span class="string">'Upper Right'</span>)</span><br><span class="line">axes[<span class="number">0</span>, <span class="number">1</span>].plot(x,<span class="string">'g'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">##设置线条</span></span><br><span class="line">axes[<span class="number">1</span>, <span class="number">0</span>].<span class="built_in">set</span>(title=<span class="string">'Lower Left'</span>)</span><br><span class="line">axes[<span class="number">1</span>, <span class="number">0</span>].plot(x,<span class="string">'g*--'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">##标记点和线条颜色分开</span></span><br><span class="line">axes[<span class="number">1</span>, <span class="number">1</span>].<span class="built_in">set</span>(title=<span class="string">'Lower Right'</span>)</span><br><span class="line">axes[<span class="number">1</span>, <span class="number">1</span>].plot(x,<span class="string">'g'</span>)</span><br><span class="line">axes[<span class="number">1</span>, <span class="number">1</span>].plot(x,<span class="string">'r*'</span>)</span><br><span class="line"></span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/d4c9a34ab336413784c696061ed460dc.png#pic_center" alt="在这里插入图片描述"></p><h3 id="Pandas"><a href="#Pandas" class="headerlink" title="Pandas"></a><a href="https://pandas.pydata.org/">Pandas</a></h3><p>Pandas 提供两种基本类型的数据结构:Series和Dataframe</p><p>Series是可以保存任何类型数据的一维数组</p><p>Dataframe 一种二维结构,用于将数据保存在包含行和列的表中</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"></span><br><span class="line">s1 = pd.Series([<span class="number">23</span>,<span class="number">324</span>,<span class="number">2</span>,<span class="number">0</span>,<span class="string">"ABC"</span>,<span class="string">"DEF"</span>,-<span class="number">123</span>])</span><br><span class="line"><span class="built_in">print</span>(s1)</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/4cb7bf87e7e247b68135f182390da732.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><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">s2 = pd.Series([<span class="number">23</span>,<span class="number">324</span>,<span class="number">2</span>,<span class="number">0</span>,<span class="string">"ABC"</span>,<span class="string">"DEF"</span>,-<span class="number">123</span>],index=[<span class="string">"a"</span>,<span class="string">"b"</span>,<span class="string">"c"</span>,<span class="string">"d"</span>,<span class="string">"e"</span>,<span class="string">"f"</span>,<span class="string">"g"</span>])</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(s2)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"我们设置的index"</span>)</span><br><span class="line"><span class="built_in">print</span>(s2[<span class="string">"b"</span>])</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/8382a524a0ad48ebb4ab5bc769b125e5.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">s3 = pd.Series(np.random.rand(<span class="number">100000</span>))</span><br><span class="line"><span class="built_in">print</span>(s3)</span><br><span class="line"></span><br><span class="line"><span class="comment">##使用pandas自带的制图函数</span></span><br><span class="line">ax = s3.plot.hist(bins=<span class="number">100</span>)</span><br><span class="line">ax.set_xlabel(<span class="string">"Number"</span>)</span><br><span class="line">ax.set_ylabel(<span class="string">"Entries per bin"</span>)</span><br><span class="line">ax.set_title(<span class="string">"Uniform distribution"</span>)</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/32647e00425a4ad9b5dcabaf44c45681.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"></span><br><span class="line"><span class="comment">##也可以使用matplotlib的</span></span><br><span class="line">plt.hist(s3,bins=<span class="number">100</span>)</span><br><span class="line">plt.title(<span class="string">"Uniform distribution"</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/0c1e5dc6afc34d1fadedf1e199ce97d1.png#pic_center" alt="在这里插入图片描述"></p><p>Dataframes</p><p>一些主要功能:<br>数据表示:以包含行和列的表格式存储数据。<br>异构数据类型:可以在不同的列(例如,整数、浮点数、字符串、布尔值)中保存不同的数据类型。<br>标签:每行和每列都有一个标签(索引和列名称)。<br>可变:允许数据操作和修改。<br>强大的操作:提供用于数据分析、操作和探索的各种功能和方法。<br>可扩展:可以通过库和用户定义的函数使用其他功能进行自定义和扩展。 </p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">df = pd.DataFrame(</span><br><span class="line">{</span><br><span class="line"> <span class="string">"Name"</span>: [<span class="string">"drunksweet"</span>, <span class="string">"jiaotangjiu"</span>,<span class="string">"soubai"</span>,<span class="string">"drunksweet"</span>, <span class="string">"jiaotangjiu"</span>,<span class="string">"soubai"</span>,],</span><br><span class="line"> <span class="string">"Age"</span>: [<span class="number">18</span>, <span class="number">19</span>, <span class="number">18</span>, <span class="number">18</span>, <span class="number">19</span>, <span class="number">18</span>],</span><br><span class="line"> <span class="string">"Sex"</span>: [<span class="string">"male"</span>, <span class="string">"male"</span>, <span class="string">"male"</span>,<span class="string">"male"</span>, <span class="string">"male"</span>, <span class="string">"male"</span>],</span><br><span class="line">})</span><br><span class="line"><span class="built_in">print</span>(df)</span><br><span class="line"></span><br><span class="line">df[<span class="string">"Age"</span>]</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/48fb2ba722e840ffae5ffe62b3ab79b0.png#pic_center" alt="在这里插入图片描述"></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">df[<span class="string">"Age"</span>].mean()</span><br></pre></td></tr></table></figure><p><img src="https://img-blog.csdnimg.cn/direct/2f917b9360464112ab1dddff45f1cfc7.png#pic_center" alt="在这里插入图片描述"></p>]]></content>
</entry>
</search>