-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
470 lines (244 loc) · 418 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>YXLazy</title>
<subtitle>工欲善其事,必先利其器</subtitle>
<link href="https://www.yxlazy.xyz/atom.xml" rel="self"/>
<link href="https://www.yxlazy.xyz/"/>
<updated>2023-01-12T08:20:42.166Z</updated>
<id>https://www.yxlazy.xyz/</id>
<author>
<name>yxlazy</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>使用Rollup,TypeScript,SCSS配置React组件库</title>
<link href="https://www.yxlazy.xyz/2023/01/03/%E4%BD%BF%E7%94%A8Rollup-TypeScript-SCSS%E9%85%8D%E7%BD%AEReact%E7%BB%84%E4%BB%B6%E5%BA%93/"/>
<id>https://www.yxlazy.xyz/2023/01/03/%E4%BD%BF%E7%94%A8Rollup-TypeScript-SCSS%E9%85%8D%E7%BD%AEReact%E7%BB%84%E4%BB%B6%E5%BA%93/</id>
<published>2023-01-03T09:10:44.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<blockquote><p>本篇为翻译的文章,由于笔者能力有限,倘若阅读体验不好,可点击原文地址查看,原文地址:<a href="https://www.codefeetime.com/post/rollup-config-for-react-component-library-with-typescript-scss/">Rollup Config for React Component Library With TypeScript + SCSS</a></p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在这篇文章中,我将尝试覆盖在构建<code>React</code>组件库时使用<a href="https://rollupjs.org/guide/en/"><code>Rollup</code></a>配置发挥作用的关键领域(特别是<code>TypeScript</code>和<code>SCSS</code>)。</p><p>同时,我将会为使用到的<a href="https://github.com/rollup/plugins"><code>Rollup</code>插件</a>写一些说明,来说明插件的作用。</p><p>我绝对不是<code>Rollup</code>的大师,也不是构建<code>React</code>组件库的权威指南。我仅仅只是分享了用于构建我自己的<code>React</code>组件库(<a href="https://github.com/DriLLFreAK100/codefee-kit">Codefee-Kit</a>)时的<code>Rollup</code>配置,这只是我自己的一个笔记,或许能帮到一些人。</p><p>顺便说一下,组件库托管在<a href="https://github.com/DriLLFreAK100/codefee-kit">Github</a>上</p><h2 id="动机"><a href="#动机" class="headerlink" title="动机"></a>动机</h2><p>在此之前,我使用<code>webpack</code>去构建库。当时,我在获得第一个工作版本时遇到了很多麻烦。我仍然记得有个属性叫”library”,为了让<code>webpack</code>对库收集信息,你需要显示设置。我很傻,在我明白整个事情之前我在谷歌上搜索了很多信息。在整个踩坑过程中投入了大量时间。我当然不喜欢那里的配置文件的复杂性。嗯,是谁?哈哈哈</p><p>尽管如此,当时的工作版本并没有得到很好的优化。我的意思是,没有配置任何代码分割,构建的输出总是一个越来越大的<code>index.js</code>文件</p><p>最近,我终于又有了一些空闲时间!因此,我决定重新审视这个话题。在<code>Webpack</code>中对如何进行代码拆分进行了一些深挖,我只是觉得它太麻烦了。</p><p>最后,我决定彻底停止,然后跳到另一个流行的构建库的选择——<code>Rollup</code>!而且,它的配置简单得多,而且节省了我很多时间!天啊,我为什么不早点跳过去?!去我的!</p><p>有一句话是这样说的,”Rollup for libraries, Webpack for apps”。事实证明,在这一点上,它仍然非常重要!</p><blockquote><p>“Rollup for libraries, Webpack for apps” <a href="https://medium.com/@PepsRyuu/why-i-use-rollup-and-not-webpack-e3ab163f4fd3">https://medium.com/@PepsRyuu/why-i-use-rollup-and-not-webpack-e3ab163f4fd3</a></p></blockquote><h2 id="主题范围"><a href="#主题范围" class="headerlink" title="主题范围"></a>主题范围</h2><p>请跳转到你感兴趣的部分</p><ol><li><a href="#rollup-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6">Rollup 配置文件</a></li><li><a href="#rollup-%E4%BB%A3%E7%A0%81%E5%88%86%E5%89%B2">Rollup 代码分割</a></li><li><a href="#rollup-%E6%8F%92%E4%BB%B6%E8%AF%B4%E6%98%8E">Rollup 插件说明</a></li></ol><h3 id="Rollup-配置文件"><a href="#Rollup-配置文件" class="headerlink" title="Rollup 配置文件"></a>Rollup 配置文件</h3><p>这是适用于我的配置文件。那里有相当多的活动部件,但我尽可能地把它清理干净,这样就不会太伤眼睛了哈哈。。。😛</p><p><strong>rollup.config.js</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><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">import</span> resolve <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/plugin-node-resolve'</span>;<br><span class="hljs-keyword">import</span> commonjs <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/plugin-commonjs'</span>;<br><span class="hljs-keyword">import</span> typescript <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/plugin-typescript'</span>;<br><br><span class="hljs-keyword">import</span> postcss <span class="hljs-keyword">from</span> <span class="hljs-string">"rollup-plugin-postcss"</span>;<br><span class="hljs-keyword">import</span> visualizer <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup-plugin-visualizer'</span>;<br><span class="hljs-keyword">import</span> { terser } <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup-plugin-terser'</span>;<br><span class="hljs-keyword">import</span> { getFiles } <span class="hljs-keyword">from</span> <span class="hljs-string">'./scripts/buildUtils'</span>;<br><br><span class="hljs-keyword">const</span> extensions = [<span class="hljs-string">'.js'</span>, <span class="hljs-string">'.ts'</span>, <span class="hljs-string">'.jsx'</span>, <span class="hljs-string">'.tsx'</span>];<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {<br> <span class="hljs-attr">input</span>: [<br> <span class="hljs-string">'./src/index.ts'</span>,<br> ...<span class="hljs-title function_">getFiles</span>(<span class="hljs-string">'./src/common'</span>, extensions),<br> ...<span class="hljs-title function_">getFiles</span>(<span class="hljs-string">'./src/components'</span>, extensions),<br> ...<span class="hljs-title function_">getFiles</span>(<span class="hljs-string">'./src/hooks'</span>, extensions),<br> ...<span class="hljs-title function_">getFiles</span>(<span class="hljs-string">'./src/utils'</span>, extensions),<br> ],<br> <span class="hljs-attr">output</span>: {<br> <span class="hljs-attr">dir</span>: <span class="hljs-string">'dist'</span>,<br> <span class="hljs-attr">format</span>: <span class="hljs-string">'esm'</span>,<br> <span class="hljs-attr">preserveModules</span>: <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">preserveModulesRoot</span>: <span class="hljs-string">'src'</span>,<br> <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,<br> },<br> <span class="hljs-attr">plugins</span>: [<br> <span class="hljs-title function_">resolve</span>(),<br> <span class="hljs-title function_">commonjs</span>(),<br> <span class="hljs-title function_">typescript</span>({<br> <span class="hljs-attr">tsconfig</span>: <span class="hljs-string">'./tsconfig.build.json'</span>,<br> <span class="hljs-attr">declaration</span>: <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">declarationDir</span>: <span class="hljs-string">'dist'</span>,<br> }),<br> <span class="hljs-title function_">postcss</span>(),<br> <span class="hljs-title function_">terser</span>(),<br> <span class="hljs-title function_">visualizer</span>({<br> <span class="hljs-attr">filename</span>: <span class="hljs-string">'bundle-analysis.html'</span>,<br> <span class="hljs-attr">open</span>: <span class="hljs-literal">true</span>,<br> }),<br> ],<br> <span class="hljs-attr">external</span>: [<span class="hljs-string">'react'</span>, <span class="hljs-string">'react-dom'</span>],<br>};<br></code></pre></td></tr></table></figure><p>正如你所看到的,为了让<code>Rollup</code>工作,你仅仅只需要导出一个<code>JSON</code>对象。当然,如果你有多个配置需要配置,你也能导出一个数组。例如构建不同的目标文件时,像<code>umd</code>,<code>cjs</code>等</p><p>我在这里配置了4个属性:</p><ul><li><code>input</code> - 打包文件的入口。可以是字符串或者字符串数组</li><li><code>output</code> - 打包文件的输出配置,例如配置打包输出的文件夹,配置<code>sourcemap</code>生成等</li><li><code>plugins</code> - 外部包调用,帮助操作、更改构建行为,例如<code>TypeScript, SCSS</code>等</li><li><code>external</code> - 不需要打包进构建包中的包,通常是指<code>peerDependencies</code>中设置的包,在我的例子中,,就是<code>react</code>和<code>react-dom</code>两个包</li></ul><h4 id="TypeScript-配置"><a href="#TypeScript-配置" class="headerlink" title="TypeScript 配置"></a>TypeScript 配置</h4><p>以下是我的<code>tsconfig</code>文件。我有2个,一个是<code>Rollup</code>用于构建,另一个类似于基本配置,主要是用于开发</p><p>背后的原因是,我想排除我的<a href="https://storybook.js.org/">Storybook stories files</a>被<code>TypeScript</code>转译。它只用于开发时,我需要<code>tsconfig</code>。因此,创建了一个带有<code>excludes</code>的单独的配置文件</p><p><strong>tsconfig.build.json</strong></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"extends"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"./tsconfig.json"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"exclude"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br> <span class="hljs-string">"node_modules"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"build"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"dist"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"scripts"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"acceptance-tests"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"webpack"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"jest"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"src/stories/**"</span><br> <span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p><strong>tsconfig.json</strong></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"compilerOptions"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"module"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"esnext"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"target"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"es5"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"lib"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span> <span class="hljs-string">"es6"</span><span class="hljs-punctuation">,</span> <span class="hljs-string">"dom"</span> <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"sourceMap"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"jsx"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"react"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"moduleResolution"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"node"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"rootDir"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"src"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"noImplicitReturns"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"noImplicitThis"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"noImplicitAny"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"strictNullChecks"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"esModuleInterop"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"baseUrl"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"src/"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"paths"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"common"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">"src/common/*"</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"components"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">"src/components/*"</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"hooks"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">"src/hooks/*"</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"utils"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">"src/utils/*"</span><span class="hljs-punctuation">]</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"exclude"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br> <span class="hljs-string">"node_modules"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"build"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"dist"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"scripts"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"acceptance-tests"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"webpack"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-string">"jest"</span><br> <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"types"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><span class="hljs-string">"typePatches"</span><span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><h3 id="Rollup-代码分割"><a href="#Rollup-代码分割" class="headerlink" title="Rollup 代码分割"></a>Rollup 代码分割</h3><p>在上面的配置中,我实际上使用了Rollup的<a href="https://rollupjs.org/guide/en/#code-splitting">代码分割 (code-splitting) </a>功能。</p><p>如果您注意到,我导出的<code>JSON</code>对象中的<code>input</code>属性实际上是字符串数组,而不是单个字符串。这实际上告诉<code>Rollup</code>将这些字符串中的每一个作为构建的单独入口点。所以,这就是代码分割构建。</p><p>数组中的所有这些字符串实际上是我在<code>src</code>文件夹中拥有的单个<code>.ts</code>和<code>.tsx</code>文件的相对路径。<code>getFiles</code>方法是我编写的一个实用方法,它帮助我深入检索所有扩展名为<code>.js、.jsx、.ts</code>和<code>.tsx</code>的文件。</p><p>下面是<a href="https://github.com/DriLLFreAK100/codefee-kit/blob/main/scripts/buildUtils.js"><code>getFiles</code></a>方法的代码:</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><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title function_">getFiles</span> = (<span class="hljs-params">entry, extensions = [], excludeExtensions = []</span>) => {<br> <span class="hljs-keyword">let</span> fileNames = [];<br> <span class="hljs-keyword">const</span> dirs = fs.<span class="hljs-title function_">readdirSync</span>(entry);<br><br> dirs.<span class="hljs-title function_">forEach</span>(<span class="hljs-function">(<span class="hljs-params">dir</span>) =></span> {<br> <span class="hljs-keyword">const</span> path = <span class="hljs-string">`<span class="hljs-subst">${entry}</span>/<span class="hljs-subst">${dir}</span>`</span>;<br><br> <span class="hljs-keyword">if</span> (fs.<span class="hljs-title function_">lstatSync</span>(path).<span class="hljs-title function_">isDirectory</span>()) {<br> fileNames = [<br> ...fileNames,<br> ...<span class="hljs-title function_">getFiles</span>(path, extensions, excludeExtensions),<br> ];<br><br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-keyword">if</span> (!excludeExtensions.<span class="hljs-title function_">some</span>(<span class="hljs-function">(<span class="hljs-params">exclude</span>) =></span> dir.<span class="hljs-title function_">endsWith</span>(exclude))<br> && extensions.<span class="hljs-title function_">some</span>(<span class="hljs-function">(<span class="hljs-params">ext</span>) =></span> dir.<span class="hljs-title function_">endsWith</span>(ext))<br> ) {<br> fileNames.<span class="hljs-title function_">push</span>(path);<br> }<br> });<br><br> <span class="hljs-keyword">return</span> fileNames;<br>};<br></code></pre></td></tr></table></figure><h3 id="Rollup-插件说明"><a href="#Rollup-插件说明" class="headerlink" title="Rollup 插件说明"></a>Rollup 插件说明</h3><p>以下是我在上面的配置文件中使用的插件,以及使用它们后我的一些解释和理解。</p><p><a href="https://www.npmjs.com/package/@rollup/plugin-node-resolve">@rollup/plugin-node-resolve</a> </p><p>对于<code>Rollup</code>,在<code>node_modules</code>中查找和捆绑第三方依赖项。这里所指的依赖关系是<code>package.json</code>文件中列出的依赖关系(<code>dependencies</code>)。</p><p><a href="https://www.npmjs.com/package/@rollup/plugin-commonjs">@rollup/plugin-commonjs</a> </p><p>对于<code>Rollup</code>,将<code>CommonJS</code>模块转换为<code>ES6</code>,以便将其包含在<code>Rollup</code>捆绑包中。它通常与<code>@rollup/plugin-node-resolve</code> 一起使用,以捆绑<code>CommonJS</code>依赖项。</p><p><a href="https://www.npmjs.com/package/@rollup/plugin-typescript">@rollup/plugin-typescript</a> </p><p>帮助以更简单的方式与<code>TypeScript</code>集成。包括将<code>TypeScript</code>转换为<code>JavaScript</code>等内容。</p><p><a href="https://www.npmjs.com/package/rollup-plugin-postcss">rollup-plugin-postcss</a></p><p>与<code>PostCSS</code>集成。对于我的用例,它有助于将<code>CSS</code>和<code>SCSS</code>文件分别构建为<code>*.CSS.js</code>和<code>*.SCSS.js</code>文件。然后,当导入相应的组件时,这些文件会相应地注入<code>HTML head</code>标签(依赖于<a href="https://www.npmjs.com/package/style-inject">style-inject</a>)</p><blockquote><p>在我目前的开发中,我已经改用样式化组件,而倾向于在<code>JS</code>模式中尝试<code>CSS</code>。</p></blockquote><p><a href="https://www.npmjs.com/package/rollup-plugin-visualizer">rollup-plugin-visualizer</a></p><p>此插件用于帮助我们分析捆绑输出。它将为我们的检查生成一个很酷的可视化。在进行捆绑包大小优化时,它特别有用,因为它可以让我们可视化捆绑包文件的各个大小。</p><p><a href="https://www.npmjs.com/package/rollup-plugin-terser">rollup-plugin-terser</a></p><p>它基本上是一个插件,用于集成<a href="https://www.npmjs.com/package/terser">terser</a>的用法。它有助于缩小和压缩我们的输出<code>JavaScript</code>文件。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在使用<code>Rollup</code>和<code>Webpack</code>构建使用<code>TypeScript</code>和<code>SCSS</code>的<code>React</code>组件库之后。。。我不得不说,只是为了这个目的而疯狂地使用<code>Rollup</code>。与<code>Webpack</code>相比,它更易于配置。</p><p>单是配置的复杂性就足以让我跳过去。我希望有一个配置文件,我可以在任何时间点轻松推理,而不是一些冗长复杂的东西,我可能会在几周内忘记正在做什么!</p><p>然而,我可能不一定对应用程序开发有同样的感觉,因为<code>Webpack</code>具有真正强大的热模块替换功能。这绝对是一个救命稻草,也是应用程序开发的必备工具。</p><p>构建工具的前景正在发生变化,如果第二天出现另一个事实上的<code>bundler</code>,并在社区中掀起风暴,这可能并不奇怪!但至少现在,<code>Rollup</code>是我的新情人😛</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ol><li><a href="https://github.com/rollup/plugins">https://github.com/rollup/plugins</a></li><li><a href="https://rollupjs.org/guide/en/">https://rollupjs.org/guide/en/</a></li><li><a href="https://marcobotto.com/blog/compiling-and-bundling-typescript-libraries-with-webpack/">https://marcobotto.com/blog/compiling-and-bundling-typescript-libraries-with-webpack/</a></li><li><a href="https://github.com/HarveyD/react-component-library">https://github.com/HarveyD/react-component-library</a></li><li><a href="https://github.com/rollup/rollup-plugin-commonjs">https://github.com/rollup/rollup-plugin-commonjs</a></li></ol>]]></content>
<summary type="html">想写一个React的组件库,但不知道该如何配置一个开发环境?这篇文章将告诉你答案</summary>
<category term="typescript" scheme="https://www.yxlazy.xyz/categories/typescript/"/>
<category term="typescript" scheme="https://www.yxlazy.xyz/tags/typescript/"/>
<category term="rollup" scheme="https://www.yxlazy.xyz/tags/rollup/"/>
</entry>
<entry>
<title>使用puppeteer获取网页信息</title>
<link href="https://www.yxlazy.xyz/2022/11/19/%E4%BD%BF%E7%94%A8puppeteer%E8%8E%B7%E5%8F%96%E7%BD%91%E9%A1%B5%E4%BF%A1%E6%81%AF/"/>
<id>https://www.yxlazy.xyz/2022/11/19/%E4%BD%BF%E7%94%A8puppeteer%E8%8E%B7%E5%8F%96%E7%BD%91%E9%A1%B5%E4%BF%A1%E6%81%AF/</id>
<published>2022-11-19T06:53:59.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><code>Puppeteer</code>的中文直译是<code>操纵木偶的人</code>,是一个提供顶层<code>API</code>来控制基于<code>DevTools Protocol</code>的<code>Chrome/Chromium</code>的<code>Node</code>库。默认,它是运行在<code>Chrome/Chromium</code>的<code>headless</code>模式下,但是也能改变它的配置,使其运行在<code>full(non-headless)</code>模式下。</p><p>总结一句话就是,<code>Puppeteer</code>就是一个运行在<code>Node</code>环境的浏览器</p><p><code>Puppeteer</code>为我们提供了丰富的能力,其中包括:</p><ul><li>为页面生成截图或者<code>PDF</code>文件</li><li>爬取<code>SPA(Single-Page Application)</code>或者<code>SSR(Sever-Side Rendering)</code></li><li>自动表单提交,<code>UI</code>测试,键盘输入等</li><li>使用最新的<code>JavaScript</code>和浏览器特性创建一个自动化的测试环境</li><li>捕获网页的<code>timeline trace</code>,帮助诊断性能问题</li><li>测试<code>Chrome</code>插件</li></ul><p>而这篇文章主要是介绍上面列出的第二点,抓取页面数据</p><h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><ul><li>首先新建一个项目文件夹叫<code>puppeteer-crawl-page-example</code>并初始化项目</li></ul><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><code class="hljs bash"><span class="hljs-built_in">mkdir</span> puppeteer-crawl-page-example<br><span class="hljs-built_in">cd</span> puppeteer-crawl-page-example && yarn init -y<br></code></pre></td></tr></table></figure><ul><li>添加<code>typescript</code>(当然如果你更喜欢直接使用<code>javascript</code>编写项目,可以直接跳过关于<code>typescript</code>的配置介绍即可)</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn add typescript ts-node nodemon --dev<br></code></pre></td></tr></table></figure><blockquote><p><code>ts-node</code>是为了动态编译<code>ts</code>文件为<code>js</code>文件,<code>nodemon</code>则是为了监听编译后的<code>js</code>文件是否发生了变化,从而决定是否重启项目</p></blockquote><ul><li><p>在<code>package.json</code>中添加运行命令,其中<code>index.ts</code>是一个文件,是我们编写代码的地方</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-comment">// 省略部分代码</span><br> <span class="hljs-attr">"script"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"start"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"nodemon --watch . --exec \"ts-node\" index.ts"</span><br> <span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure></li><li><p>安装<code>Puppeteer</code>,当我们安装的时候,它会自动安装一个最新版的<code>Chromium</code>,以保证<code>Puppeteer</code>能正常的工作,所以对于无法安装的,可能需要借助科学上网工具,当然也有其他解决方式就自行百度啦</p></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn add puppeteer<br></code></pre></td></tr></table></figure><ul><li>安装成功后,在<code>index.ts</code>文件中,首先我们需要先启动<code>(launch)</code>一个浏览器实例<code>(Browser)</code>,也就是调用<code>Puppeteer</code>的<code>launch()</code>方法创建一个<code>Browser</code>对象</li></ul><blockquote><p><code>Puppeteer</code>的<code>API</code>都是<code>Promise</code>类型</p></blockquote><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs typescript">(<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-comment">// 启动一个浏览器, launch接收一个options用于配置信息</span><br> <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> puppeteer.<span class="hljs-title function_">launch</span>({<br> <span class="hljs-comment">// 是否在headless模式下运行浏览器</span><br> <span class="hljs-attr">headless</span>: <span class="hljs-literal">false</span>,<br> <span class="hljs-comment">// 是否打开Devtool,如果设置为true,headless将强制为false</span><br> <span class="hljs-attr">devtools</span>: <span class="hljs-literal">true</span>,<br> });<br><br> <span class="hljs-comment">// 在browser上下文中,创建一个Page对象</span><br> <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.<span class="hljs-title function_">newPage</span>();<br><br> <span class="hljs-comment">// 关闭Chromium和所有的Page</span><br> <span class="hljs-keyword">await</span> browser.<span class="hljs-title function_">close</span>();<br>}());<br></code></pre></td></tr></table></figure><ul><li>启动项目<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn run start<br></code></pre></td></tr></table></figure>当看到以下信息,则说明项目启动成功,并且你会看到打开了浏览器,并且马上关闭了<figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs awk">$ nodemon --watch . --exec <span class="hljs-string">"ts-node"</span> index.ts<br>[nodemon] <span class="hljs-number">2.0</span>.<span class="hljs-number">20</span><br>[nodemon] to restart at any time, enter `rs`<br>[nodemon] watching path(s): *.*<br>[nodemon] watching extensions: ts,json<br>[nodemon] starting `ts-node index.ts`<br>[nodemon] clean <span class="hljs-keyword">exit</span> - waiting <span class="hljs-keyword">for</span> changes before restart<br></code></pre></td></tr></table></figure></li></ul><h2 id="小试牛刀"><a href="#小试牛刀" class="headerlink" title="小试牛刀"></a>小试牛刀</h2><p>我们先来写个简单的截图功能,主要是使用<code>Page</code>实例上的<code>screenshot()</code>方法</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs typescript"><span class="hljs-comment">// ...</span><br><br><span class="hljs-comment">// 加载页面</span><br><span class="hljs-keyword">await</span> page.<span class="hljs-title function_">goto</span>(<span class="hljs-string">'https://blog.csdn.net/qq_45484646?type=blog'</span>, {<br> <span class="hljs-comment">// 超时时间,0为禁用超时</span><br> <span class="hljs-comment">// timeout: 0,</span><br>})<br><br><span class="hljs-comment">// 对当前页面进行截图</span><br><span class="hljs-keyword">await</span> page.<span class="hljs-title function_">screenshot</span>({<br> <span class="hljs-attr">type</span>: <span class="hljs-string">'png'</span>,<br> <span class="hljs-comment">// 保存文件的路径</span><br> <span class="hljs-attr">path</span>: <span class="hljs-string">'screenshot.png'</span>,<br> <span class="hljs-comment">// 截取整个可滚动页面的屏幕截图</span><br> <span class="hljs-comment">// fullPage: true</span><br>});<br><br><span class="hljs-comment">// ...</span><br></code></pre></td></tr></table></figure><p>运行结束后,你会看到在项目的根目录下多了个<code>screenshot.png</code>文件,点开你会发现和我们在正常的浏览器中输入的链接加载出来的页面是一模一样,是不是感受到了<code>Puppeteer</code>的强大🚀</p><h2 id="大显神通"><a href="#大显神通" class="headerlink" title="大显神通"></a>大显神通</h2><p>接下来,我们可以使用<code>Puppeteer</code>来抓取页面信息,但在抓取页面之前我们需要知道要抓取的内容的信息,也就是该怎么访问这些元素。<br><img src="/2022/11/19/%E4%BD%BF%E7%94%A8puppeteer%E8%8E%B7%E5%8F%96%E7%BD%91%E9%A1%B5%E4%BF%A1%E6%81%AF/79131668853921_.pic.jpg" alt="页面元素分析"></p><p>我们需要获取整个文章列表(也就是<code><div class="mainContent"></div></code>中的<code>article</code>标签中的内容),并获取每个文章的<code>title, description</code>和阅读数。</p><p>整个代码如下:</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-comment">// ...</span><br><br><span class="hljs-comment">// 加载页面</span><br><span class="hljs-keyword">await</span> page.<span class="hljs-title function_">goto</span>(<span class="hljs-string">'https://blog.csdn.net/qq_45484646?type=blog'</span>, {<br> <span class="hljs-comment">// 超时时间,0为禁用超时</span><br> <span class="hljs-comment">// timeout: 0,</span><br>});<br><br><span class="hljs-comment">// 相当于浏览器中的document.querySelectorAll,但是返回的是 ElementHandle 类型</span><br><span class="hljs-keyword">const</span> articles = <span class="hljs-keyword">await</span> page.$$(<span class="hljs-string">'.mainContent article a'</span>);<br><span class="hljs-comment">// 用于保存一组Promise,方便Promise.all直接处理</span><br><span class="hljs-keyword">const</span> <span class="hljs-attr">collects</span>: <span class="hljs-built_in">any</span>[] = [];<br><br><span class="hljs-comment">// 获取文章信息</span><br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> article <span class="hljs-keyword">of</span> articles) { <br> <span class="hljs-comment">// evaluate(),对Page上下文进行计算,并返回一个值</span><br> collects.<span class="hljs-title function_">push</span>(<span class="hljs-keyword">await</span> page.evaluate(<span class="hljs-function"><span class="hljs-params">article</span> =></span> {<br> <span class="hljs-comment">// 这里的代码是放到Page 上下文中执行的,所以在这里是不能访问外部的作用域(也就是Node环境)</span><br><br> <span class="hljs-comment">// 获取文章标题节点</span><br> <span class="hljs-keyword">const</span> title = article.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'h4'</span>);<br> <span class="hljs-comment">// 获取文章描述节点</span><br> <span class="hljs-keyword">const</span> description = article.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.blog-list-content'</span>) <span class="hljs-keyword">as</span> <span class="hljs-title class_">HTMLDivElement</span>;<br> <span class="hljs-comment">// 获取文章阅读数节点</span><br> <span class="hljs-keyword">const</span> readNum = article.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'.blog-list-footer .view-num'</span>);<br><br> <span class="hljs-comment">// 提取我们需要的文章信息</span><br> <span class="hljs-keyword">return</span> {<br> <span class="hljs-attr">title</span>: title?.<span class="hljs-property">innerText</span>, <br> <span class="hljs-attr">description</span>: description?.<span class="hljs-property">innerText</span>, <br> <span class="hljs-attr">readNum</span>: readNum?.<span class="hljs-property">childNodes</span>[<span class="hljs-number">0</span>].<span class="hljs-property">textContent</span>,<br> };<br> }, article));<br>}<br><br><span class="hljs-comment">// 等待所有数据成功返回</span><br><span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">all</span>(collects);<br><br><span class="hljs-comment">// 输出获取的数据到控制台</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'[Data]\t'</span>, data);<br><br><span class="hljs-comment">// ...</span><br></code></pre></td></tr></table></figure><p>可以看到控制台输出了我们想要的结果</p><p><img src="/2022/11/19/%E4%BD%BF%E7%94%A8puppeteer%E8%8E%B7%E5%8F%96%E7%BD%91%E9%A1%B5%E4%BF%A1%E6%81%AF/WeChat473375ab8df6ba9c3a2bb6419437eef2.png"></p><p>🎉恭喜,到此为止本篇教程就已结束</p><blockquote><p>获取完整代码 <a href="https://github.com/yxlazy/puppeteer-crawl-page-example">https://github.com/yxlazy/puppeteer-crawl-page-example</a></p></blockquote><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>现在我们来做个简单的总结,首先我们简单介绍了<code>Puppeteer</code>是什么,它是一个运行在<code>Node</code>环境下的浏览器,为我们提供了丰富的能力,例如为页面截图等,然后在这里我们通过介绍如何抓取页面的数据从而简单的感受了<code>Puppeteer</code>强大的功能之一</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>[1] <a href="https://pptr.dev/">https://pptr.dev/</a></p>]]></content>
<summary type="html">Puppeteer 是一个运行在Node端的浏览器,为我们提供了强大的功能,例如抓取页面数据,对页面进行截图等功能</summary>
<category term="javascript" scheme="https://www.yxlazy.xyz/categories/javascript/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
<category term="puppeteer" scheme="https://www.yxlazy.xyz/tags/puppeteer/"/>
</entry>
<entry>
<title>问:如何获取文本内容并显示在前端页面🤔</title>
<link href="https://www.yxlazy.xyz/2022/11/04/%E9%97%AE%EF%BC%9A%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E6%96%87%E6%9C%AC%E5%86%85%E5%AE%B9%E5%B9%B6%E6%98%BE%E7%A4%BA%E5%9C%A8%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2%F0%9F%A4%94/"/>
<id>https://www.yxlazy.xyz/2022/11/04/%E9%97%AE%EF%BC%9A%E5%A6%82%E4%BD%95%E8%8E%B7%E5%8F%96%E6%96%87%E6%9C%AC%E5%86%85%E5%AE%B9%E5%B9%B6%E6%98%BE%E7%A4%BA%E5%9C%A8%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2%F0%9F%A4%94/</id>
<published>2022-11-04T06:52:16.000Z</published>
<updated>2023-01-12T08:20:42.174Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>今天在水友群里看到有个前端小伙伴在群里发了个问题,“后端给我返了<code>.txt</code>文本的地址,我怎么通过这个地址获取文本内容显示在页面上呀”,接着后面的群友的回答语出惊人,“工资给我,我来帮你”。“蛤,有这么简单吗”,我心头顿时冒出这个疑问。这不巧了吗,我正在看<code>ArrayBuffer</code>的知识,看到说<code>FileReader API</code>有个方法—<code>readAsArrayBuffer</code>,能返回一个<code>ArrayBuffer</code>对象,然后就写了个示例去读取上传的文件…… 结果还真有那么简单。</p><p>好吧,前面的内容和主题基本没啥太大关系,下面进入我们的正餐环节</p><h2 id="FileReader"><a href="#FileReader" class="headerlink" title="FileReader"></a>FileReader</h2><p><code>FileReader</code>对象允许<code>Web</code>应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用<code>File</code>或<code>Blob</code>对象指定要读取的文件或数据。</p><p>其中<code>File</code>对象来自用户在一个<code>input</code>元素上选择文件后返回的<code>FileList</code>对象,也可以来自拖放操作生成的<code>DataTransfer</code>对象,还可以是来自在一个<code>HTMLCanvasElement</code>上执行<code>mozGetAsFile()</code>方法后返回结果。</p><blockquote><p>以上内容来自 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader">MDN FileReader</a> 😀</p></blockquote><p><code>FileReader</code>为我们提供了几个方法,用于读取数据:</p><ul><li><code>readAsArrayBuffer(blob)</code>: 用于将指定的<code>Blob</code>或<code>File</code>内容读取成<code>ArrayBuffer</code>数据对象</li><li><code>readAsText(blob[, encoding])</code>: 用于将指定的<code>Blob</code>或<code>File</code>读取成字符串内容</li><li><code>readAsDataURL(blob)</code>: 用于将指定的<code>Blob</code>或<code>File</code>读取成包含<code>data: URL</code>格式的<code>base64</code>字符串内容</li></ul><p>一旦读取完成,将会触发一个<code>load</code>事件,并且可以通过<code>reader.result</code>的方式获取到读取的内容</p><h2 id="读取文件"><a href="#读取文件" class="headerlink" title="读取文件"></a>读取文件</h2><p>现在我们知道了<code>FileReader</code>能读取<code>File</code>和<code>Blob</code>对象指定的数据,首先我们需要先创建一个<code>FileReader</code>实例</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> reader = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileReader</span>()<br></code></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> fileInput = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'input[type="file"]'</span>);<br><br><span class="hljs-comment">// 监听选择文件</span><br>fileInput.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'change'</span>, <span class="hljs-function"><span class="hljs-params">e</span> =></span> {<br> <span class="hljs-comment">// 获取选中的文件,这里是单选</span><br> <span class="hljs-keyword">const</span> file = e.<span class="hljs-property">target</span>.<span class="hljs-property">files</span>[<span class="hljs-number">0</span>]<br><br> <span class="hljs-comment">// 读取文件</span><br> reader.<span class="hljs-title function_">readAsText</span>(file)<br><br> <span class="hljs-comment">// 监听读取完成</span><br> reader.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'load'</span>, <span class="hljs-function">() =></span> {<br> <span class="hljs-comment">// 获取读取的文件内容</span><br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(reader.<span class="hljs-property">result</span>)<br> })<br>})<br></code></pre></td></tr></table></figure><p>以上就完成了我们在本地选择的文件的读取操作</p><p>回到前面,我们遇到的问题是“后端给我返了<code>.txt</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><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><code class="hljs javascript"><span class="hljs-title function_">fetch</span>(<span class="hljs-string">'http://localhost/test.txt'</span>)<br><span class="hljs-comment">// 响应数据转换成blob对象</span><br>.<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">res</span> =></span> res.<span class="hljs-title function_">blob</span>())<br>.<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">blob</span> =></span> {<br> <span class="hljs-comment">// 读取blob</span><br> reader.<span class="hljs-title function_">readAsText</span>(blob)<br><br> <span class="hljs-comment">// 监听读取完成</span><br> reader.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'load'</span>, <span class="hljs-function">() =></span> {<br> <span class="hljs-comment">// 获取读取的文件内容</span><br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(reader.<span class="hljs-property">result</span>)<br> })<br>})<br></code></pre></td></tr></table></figure><p>剩下的显示到前端页面,就看具体逻辑实现了</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p><code>FileReader</code> 对象为我们提供了读取文件的能力,只要将需要读取的文件转换成<code>Blob</code>对象,我们就能获取文件的内容,然后剩下的,就是对文件读取的内容进行相应的解码操作</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader">https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader</a></p>]]></content>
<summary type="html">我们该如何从一个连接中提取出里面文本内容,并显示在前端页面上呢?🤔</summary>
<category term="javascript" scheme="https://www.yxlazy.xyz/categories/javascript/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
</entry>
<entry>
<title>react-tiny-virtual-list源码解析</title>
<link href="https://www.yxlazy.xyz/2022/10/25/react-tiny-virtual-list%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
<id>https://www.yxlazy.xyz/2022/10/25/react-tiny-virtual-list%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/</id>
<published>2022-10-25T02:34:06.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>所谓虚拟列表,就是只渲染用户能实际看到的内容,也就是我们所说的视口,对于超过视口的部分就不渲染,从而避免渲染元素过多造成页面卡顿,大概效果如下</p><img src="https://upload-images.jianshu.io/upload_images/20672535-fe03aee58f31b71d.png?imageMogr2/auto-orient/strip|imageView2/2/w/720/format/webp"><blockquote><p>图片来自这里<a href="https://www.jianshu.com/p/39404c94dbd0">https://www.jianshu.com/p/39404c94dbd0</a></p></blockquote><h2 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h2><p><code>react-tiny-virtual-list</code>支持垂直和水平方向上滚动,通过组件提供的<code>recomputeSizes()</code>方法,可支持加载动态高度的元素</p><h3 id="部分参数分析"><a href="#部分参数分析" class="headerlink" title="部分参数分析"></a>部分参数分析</h3><p><code>width</code>和<code>height</code> 根据滚动的方向设置列表的宽度或者高度<br><code>itemCount</code> 指定渲染列表的总数<br><code>itemSize</code>支持纯数值,或者指定每一项元素高度的数据,或者一个返回高度的函数<br><code>renderItem</code> 渲染元素,其参数包含了当前元素的<code>index</code>和<code>style</code><br><code>overscanCount</code> 要在可见项上方/下方渲染的额外缓冲区项数。调整此项可以帮助减少某些浏览器/设备上的滚动闪烁。<br><code>estimatedItemSize</code> 估算的元素高度,用来支持动态高度</p><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><p>首先我们看下组件的<code>render()</code>方法的实现</p> <figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">const</span> <span class="hljs-attr">STYLE_WRAPPER</span>: <span class="hljs-title class_">React</span>.<span class="hljs-property">CSSProperties</span> = {<br> <span class="hljs-attr">overflow</span>: <span class="hljs-string">'auto'</span>,<br> <span class="hljs-attr">willChange</span>: <span class="hljs-string">'transform'</span>,<br> <span class="hljs-title class_">WebkitOverflowScrolling</span>: <span class="hljs-string">'touch'</span>,<br>};<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">STYLE_INNER</span>: <span class="hljs-title class_">React</span>.<span class="hljs-property">CSSProperties</span> = {<br> <span class="hljs-attr">position</span>: <span class="hljs-string">'relative'</span>,<br> <span class="hljs-attr">width</span>: <span class="hljs-string">'100%'</span>,<br> <span class="hljs-attr">minHeight</span>: <span class="hljs-string">'100%'</span>,<br>};<br><span class="hljs-comment">// ...</span><br><span class="hljs-title function_">render</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">const</span> {<br> estimatedItemSize,<br> height,<br> overscanCount = <span class="hljs-number">3</span>,<br> renderItem,<br> itemCount,<br> itemSize,<br> onItemsRendered,<br> onScroll,<br> scrollToIndex,<br> scrollToAlignment,<br> stickyIndices,<br> style,<br> width,<br> ...props<br> } = <span class="hljs-variable language_">this</span>.<span class="hljs-property">props</span>;<br> <span class="hljs-comment">// 当前距离index为0的偏移,其计算值为,itemSize * 已经滚动的元素个数</span><br> <span class="hljs-keyword">const</span> {offset} = <span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span>;<br> <span class="hljs-comment">// 当前要渲染的元素start index 和stop index</span><br> <span class="hljs-comment">// sizeAndPositionManager 是 SizeAndPositionManager 的实例,用来计算渲染到页面的元素的工具</span><br> <span class="hljs-keyword">const</span> {start, stop} = <span class="hljs-variable language_">this</span>.<span class="hljs-property">sizeAndPositionManager</span>.<span class="hljs-title function_">getVisibleRange</span>({<br> <span class="hljs-attr">containerSize</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">props</span>[sizeProp[scrollDirection]] || <span class="hljs-number">0</span>,<br> offset,<br> overscanCount,<br> });<br> <span class="hljs-keyword">const</span> <span class="hljs-attr">items</span>: <span class="hljs-title class_">React</span>.<span class="hljs-property">ReactNode</span>[] = [];<br> <span class="hljs-comment">// 最外层容器的样式</span><br> <span class="hljs-keyword">const</span> wrapperStyle = {...<span class="hljs-variable constant_">STYLE_WRAPPER</span>, ...style, height, width};<br> <span class="hljs-comment">// 每个元素的样式</span><br> <span class="hljs-keyword">const</span> innerStyle = {<br> ...<span class="hljs-variable constant_">STYLE_INNER</span>,<br> [sizeProp[scrollDirection]]: <span class="hljs-variable language_">this</span>.<span class="hljs-property">sizeAndPositionManager</span>.<span class="hljs-title function_">getTotalSize</span>(),<br> };<br><br> <span class="hljs-comment">// 设置元素的position为sticky</span><br> <span class="hljs-keyword">if</span> (stickyIndices != <span class="hljs-literal">null</span> && stickyIndices.<span class="hljs-property">length</span> !== <span class="hljs-number">0</span>) {<br> stickyIndices.<span class="hljs-title function_">forEach</span>(<span class="hljs-function">(<span class="hljs-params">index: <span class="hljs-built_in">number</span></span>) =></span><br> items.<span class="hljs-title function_">push</span>(<br> <span class="hljs-title function_">renderItem</span>({<br> index,<br> <span class="hljs-attr">style</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getStyle</span>(index, <span class="hljs-literal">true</span>),<br> }),<br> ),<br> );<br><br> <span class="hljs-keyword">if</span> (scrollDirection === <span class="hljs-variable constant_">DIRECTION</span>.<span class="hljs-property">HORIZONTAL</span>) {<br> innerStyle.<span class="hljs-property">display</span> = <span class="hljs-string">'flex'</span>;<br> }<br> }<br> <span class="hljs-comment">// 要渲染的元素,传递index和style</span><br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> start !== <span class="hljs-string">'undefined'</span> && <span class="hljs-keyword">typeof</span> stop !== <span class="hljs-string">'undefined'</span>) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> index = start; index <= stop; index++) {<br> <span class="hljs-keyword">if</span> (stickyIndices != <span class="hljs-literal">null</span> && stickyIndices.<span class="hljs-title function_">includes</span>(index)) {<br> <span class="hljs-keyword">continue</span>;<br> }<br><br> items.<span class="hljs-title function_">push</span>(<br> <span class="hljs-title function_">renderItem</span>({<br> index,<br> <span class="hljs-attr">style</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getStyle</span>(index, <span class="hljs-literal">false</span>),<br> }),<br> );<br> }<br> <span class="hljs-comment">// 这是个回调函数</span><br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> onItemsRendered === <span class="hljs-string">'function'</span>) {<br> <span class="hljs-title function_">onItemsRendered</span>({<br> <span class="hljs-attr">startIndex</span>: start,<br> <span class="hljs-attr">stopIndex</span>: stop,<br> });<br> }<br> }<br><br> <span class="hljs-keyword">return</span> (<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{this.getRef}</span> {<span class="hljs-attr">...props</span>} <span class="hljs-attr">style</span>=<span class="hljs-string">{wrapperStyle}</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{innerStyle}</span>></span>{items}<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br> );<br>}<br></code></pre></td></tr></table></figure><p>可以看到,这里最主要的操作是获取当前要渲染的元素,而这个操作是通过<code>this.sizeAndPositionManager.getVisibleRange()</code>函数计算出来的,而<code>this.sizeAndPositionManager</code>是在组件初始化时由<code>SizeAndPositionManager</code>创建的实例</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-comment">// ...</span><br>sizeAndPositionManager = <span class="hljs-keyword">new</span> <span class="hljs-title class_">SizeAndPositionManager</span>({<br> <span class="hljs-comment">// 列表总个数</span><br> <span class="hljs-attr">itemCount</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-property">props</span>.<span class="hljs-property">itemCount</span>,<br> <span class="hljs-comment">// 调用会返回列表的元素大小</span><br> <span class="hljs-attr">itemSizeGetter</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">itemSizeGetter</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">props</span>.<span class="hljs-property">itemSize</span>),<br> <span class="hljs-comment">// 列表元素的估算大小</span><br> <span class="hljs-attr">estimatedItemSize</span>: <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getEstimatedItemSize</span>(),<br>});<br><span class="hljs-comment">// ...</span><br>itemSizeGetter = <span class="hljs-function">(<span class="hljs-params">itemSize: Props[<span class="hljs-string">'itemSize'</span>]</span>) =></span> {<br> <span class="hljs-comment">// 可以看到获取元素大小,其实获取的是itemSize并不是estimatedItemSize</span><br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-params">index</span> =></span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getSize</span>(index, itemSize);<br>};<br></code></pre></td></tr></table></figure><p><code>SizeAndPositionManager</code>是一个工具类,用于处理列表元素的信息,其构造函数,用于初始化一些变量,其中<code>this.itemSizeAndPositionData</code>用于缓存<code>item</code>的<code>size</code>和<code>offset</code>,<code>this.lastMeasuredIndex</code>用于保存最后一个计算元素时的<code>index</code></p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SizeAndPositionManager</span> {<br> <span class="hljs-keyword">private</span> <span class="hljs-attr">itemSizeGetter</span>: <span class="hljs-title class_">ItemSizeGetter</span>;<br> <span class="hljs-keyword">private</span> <span class="hljs-attr">itemCount</span>: <span class="hljs-built_in">number</span>;<br> <span class="hljs-keyword">private</span> <span class="hljs-attr">estimatedItemSize</span>: <span class="hljs-built_in">number</span>;<br> <span class="hljs-keyword">private</span> <span class="hljs-attr">lastMeasuredIndex</span>: <span class="hljs-built_in">number</span>;<br> <span class="hljs-keyword">private</span> <span class="hljs-attr">itemSizeAndPositionData</span>: <span class="hljs-title class_">SizeAndPositionData</span>;<br><br> <span class="hljs-title function_">constructor</span>(<span class="hljs-params">{itemCount, itemSizeGetter, estimatedItemSize}: Options</span>) {<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">itemSizeGetter</span> = itemSizeGetter;<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">itemCount</span> = itemCount;<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">estimatedItemSize</span> = estimatedItemSize;<br><br> <span class="hljs-comment">// Cache of size and position data for items, mapped by item index.</span><br> <span class="hljs-comment">// 根据index,缓存item的大小和位置</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">itemSizeAndPositionData</span> = {};<br> <span class="hljs-comment">// Measurements for items up to this index can be trusted; items afterward should be estimated.</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">lastMeasuredIndex</span> = -<span class="hljs-number">1</span>;<br> }<br> <span class="hljs-comment">// ...</span><br>}<br></code></pre></td></tr></table></figure><p><code>getVisibleRange()</code>是用来获取当前渲染元素的<code>start index</code>和<code>stop index</code></p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-title function_">getVisibleRange</span>({<br> <span class="hljs-comment">// 容器大小</span><br> containerSize,<br> <span class="hljs-comment">// 当前偏移</span><br> offset,<br> <span class="hljs-comment">// 额外多加载数据的个数</span><br> overscanCount,<br>}: {<br> <span class="hljs-attr">containerSize</span>: <span class="hljs-built_in">number</span>;<br> <span class="hljs-attr">offset</span>: <span class="hljs-built_in">number</span>;<br> <span class="hljs-attr">overscanCount</span>: <span class="hljs-built_in">number</span>;<br>}): {start?: <span class="hljs-built_in">number</span>; stop?: <span class="hljs-built_in">number</span>} {<br> <span class="hljs-comment">// 计算元素总的高度</span><br> <span class="hljs-keyword">const</span> totalSize = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getTotalSize</span>();<br><br> <span class="hljs-keyword">if</span> (totalSize === <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> {};<br> }<br> <span class="hljs-comment">// 最大偏移</span><br> <span class="hljs-keyword">const</span> maxOffset = offset + containerSize;<br> <span class="hljs-comment">// 查找与offset距离最近的元素的index,作为视口显示的第一个元素</span><br> <span class="hljs-keyword">let</span> start = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">findNearestItem</span>(offset);<br><br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> start === <span class="hljs-string">'undefined'</span>) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`Invalid offset <span class="hljs-subst">${offset}</span> specified`</span>);<br> }<br> <span class="hljs-comment">// 获取当前start处 元素的信息</span><br> <span class="hljs-keyword">const</span> datum = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getSizeAndPositionForIndex</span>(start);<br> offset = datum.<span class="hljs-property">offset</span> + datum.<span class="hljs-property">size</span>;<br><br> <span class="hljs-keyword">let</span> stop = start;<br> <span class="hljs-comment">// 计算视口显示的最后一个元素的index</span><br> <span class="hljs-comment">// 计算下一个元素的offset,等于当前元素的size + 当前元素的offset</span><br> <span class="hljs-keyword">while</span> (offset < maxOffset && stop < <span class="hljs-variable language_">this</span>.<span class="hljs-property">itemCount</span> - <span class="hljs-number">1</span>) {<br> stop++;<br> offset += <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getSizeAndPositionForIndex</span>(stop).<span class="hljs-property">size</span>;<br> }<br> <span class="hljs-comment">// 加载额外的数据,减少滚动闪烁</span><br> <span class="hljs-keyword">if</span> (overscanCount) {<br> start = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">max</span>(<span class="hljs-number">0</span>, start - overscanCount);<br> stop = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">min</span>(stop + overscanCount, <span class="hljs-variable language_">this</span>.<span class="hljs-property">itemCount</span> - <span class="hljs-number">1</span>);<br> }<br><br> <span class="hljs-keyword">return</span> {<br> start,<br> stop,<br> };<br>}<br></code></pre></td></tr></table></figure><p>组件在<code>componentDidMount</code>生命周期中,为容器组件挂载了滚动事件,用于处理滚动。当滚动触发时,去更新<code>offset</code>状态,从而触发新的计算后重新渲染元素</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-title function_">componentDidMount</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">const</span> {scrollOffset, scrollToIndex} = <span class="hljs-variable language_">this</span>.<span class="hljs-property">props</span>;<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">rootNode</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'scroll'</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">handleScroll</span>, {<br> <span class="hljs-attr">passive</span>: <span class="hljs-literal">true</span>,<br> });<br><br> <span class="hljs-comment">// ...</span><br>}<br><br><span class="hljs-comment">//...</span><br><br><span class="hljs-keyword">private</span> handleScroll = <span class="hljs-function">(<span class="hljs-params">event: UIEvent</span>) =></span> {<br> <span class="hljs-keyword">const</span> {onScroll} = <span class="hljs-variable language_">this</span>.<span class="hljs-property">props</span>;<br> <span class="hljs-comment">// 获取偏移</span><br> <span class="hljs-keyword">const</span> offset = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getNodeOffset</span>();<br> <span class="hljs-keyword">if</span> (<br> offset < <span class="hljs-number">0</span> ||<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">state</span>.<span class="hljs-property">offset</span> === offset ||<br> event.<span class="hljs-property">target</span> !== <span class="hljs-variable language_">this</span>.<span class="hljs-property">rootNode</span><br> ) {<br> <span class="hljs-keyword">return</span>;<br> }<br> <span class="hljs-comment">// 更新offset,触发rerender</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">setState</span>({<br> offset,<br> <span class="hljs-attr">scrollChangeReason</span>: <span class="hljs-variable constant_">SCROLL_CHANGE_REASON</span>.<span class="hljs-property">OBSERVED</span>,<br> });<br><br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> onScroll === <span class="hljs-string">'function'</span>) {<br> <span class="hljs-title function_">onScroll</span>(offset, event);<br> }<br>};<br><span class="hljs-comment">// 容器元素的滚动距离(scrollTop/scrollLeft),就是当前的offset</span><br><span class="hljs-keyword">private</span> <span class="hljs-title function_">getNodeOffset</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">const</span> {scrollDirection = <span class="hljs-variable constant_">DIRECTION</span>.<span class="hljs-property">VERTICAL</span>} = <span class="hljs-variable language_">this</span>.<span class="hljs-property">props</span>;<br><br> <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">rootNode</span>[scrollProp[scrollDirection]];<br>}<br></code></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过对源码的分析,可以看出,根据<code>start</code>和<code>stop</code>就可以计算出当前需要渲染的元素,要得到<code>start</code>值,就需要计算元素的偏移量<code>(offset)</code>,若要得到<code>stop</code>,需要将<code>start</code>之前的偏移量<code>(offset)</code>再加上容器的高度或宽度。在滚动时,需要监听容器的<code>scroll</code>事件,容器的<code>scrollTop/scrollLeft</code>就是当前要展示在容器中的元素的偏移量<code>(offset)</code>,然后再用前面的计算方式,就可以得到新的<code>start</code>和<code>stop</code></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://www.jianshu.com/p/39404c94dbd0">https://www.jianshu.com/p/39404c94dbd0</a></p><p><a href="https://github.com/dwqs/blog/issues/71">https://github.com/dwqs/blog/issues/71</a></p><p><a href="https://github.com/clauderic/react-tiny-virtual-list">https://github.com/clauderic/react-tiny-virtual-list</a></p>]]></content>
<summary type="html">对于长列表的渲染,通常的优化手段就是分页、懒加载和虚拟列表。而虚拟列表一直都是面试中的常见题,现在我们通过阅读react-tiny-virtual-list的源码来看看我们应该如何处理虚拟列表</summary>
<category term="react" scheme="https://www.yxlazy.xyz/categories/react/"/>
<category term="源码解析" scheme="https://www.yxlazy.xyz/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
</entry>
<entry>
<title>使用nodejs创建websocket</title>
<link href="https://www.yxlazy.xyz/2022/10/13/%E4%BD%BF%E7%94%A8nodejs%E5%88%9B%E5%BB%BAwebsocket/"/>
<id>https://www.yxlazy.xyz/2022/10/13/%E4%BD%BF%E7%94%A8nodejs%E5%88%9B%E5%BB%BAwebsocket/</id>
<published>2022-10-13T06:12:14.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>本文主题是阅读<code>nodejs-websocket</code>库的源码,进而搞懂<code>websocket</code>的实现方式,并通过自己手写搭建一个简单的<code>websocket</code>服务</p><p><code>Web Scoket</code>是一个基于<code>TCP</code>协议的全双工的通信方式,当建立链接后,其服务端可以直接发起对客户端的通信</p><h2 id="工作方式"><a href="#工作方式" class="headerlink" title="工作方式"></a>工作方式</h2><p>要建立一个<code>Web Socket</code>链接,首先需要通过<code>HTTP</code>进行握手,在握手成功后,切换协议方式(<code>HTTP</code>切换成 <code>Web Socket</code>协议),链接建立成功,就可以开始通信了。</p><p>在握手时,客户端需要在请求报文中注明自己需要切换协议为<code>Web Socket</code>协议(在<code>Web Socket</code>中,我们不需要关注请求报文,因为当我们通过 <code>new WebSocket(ws://example.com/test)</code>,创建实例时,浏览器会在内部为我们处理请求的报文)</p><blockquote><p>同源策略不适用于<code>Web Socket</code></p></blockquote><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs http"><span class="hljs-keyword">GET</span> <span class="hljs-string">/test</span> <span class="hljs-meta">HTTP/1.1</span><br><span class="hljs-attribute">Connection</span><span class="hljs-punctuation">: </span>Upgrade<br><span class="hljs-attribute">Upgrade</span><span class="hljs-punctuation">: </span>websocket<br><span class="hljs-attribute">Sec-WebSocket-Version</span><span class="hljs-punctuation">: </span>13<br><span class="hljs-attribute">Sec-WebSocket-Key</span><span class="hljs-punctuation">: </span>udGGqBU9BqSiThHTKc0B6g==<br></code></pre></td></tr></table></figure><p>当服务端收到新的<code>Web Scoket</code>链接时,判断当前发起的是<code>Web Socket</code>链接后,然后需要返回一个响应报文,来告诉客户端切换到<code>Web Socket</code>协议</p><figure class="highlight http"><table><tr><td class="gutter"><pre><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><code class="hljs http"><span class="hljs-meta">HTTP/1.1</span> <span class="hljs-number">101</span> Switching Protocols<br><span class="hljs-attribute">Upgrade</span><span class="hljs-punctuation">: </span>websocket<br><span class="hljs-attribute">Connection</span><span class="hljs-punctuation">: </span>Upgrade<br><span class="hljs-attribute">Sec-WebSocket-Accept</span><span class="hljs-punctuation">: </span>4UCGeYLdUGEUaei+36wTyP60Mcg=<br></code></pre></td></tr></table></figure><blockquote><ul><li><code>HTTP 101</code> 状态码英文名称是<code>Switching Protocols</code>,表示切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议</li><li><code>Sec-WebSocket-Accept</code> 用以告知服务器愿发起一个 <code>websocket</code> 连接。<code>Sec-WebSocket-Accept</code>的计算方法:<code>base64(hsa1(sec-websocket-key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11))</code>。参考:<a href="https://www.zhihu.com/question/67784701">https://www.zhihu.com/question/67784701</a></li></ul></blockquote><h2 id="源码解读"><a href="#源码解读" class="headerlink" title="源码解读"></a>源码解读</h2><blockquote><p>以下只列出了关键性代码</p></blockquote><h3 id="握手"><a href="#握手" class="headerlink" title="握手"></a>握手</h3><p><code>Server</code>类,用于创建一个<code>server</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">Server</span>(<span class="hljs-params">secure, options, callback</span>) {<br> <span class="hljs-comment">// 内部server,收到一个"connection"事件后调用</span><br> <span class="hljs-comment">// 这个事件是nodejs 服务的事件,与下面触发的"connection"事件不是一个</span><br><span class="hljs-keyword">var</span> onConnection = <span class="hljs-keyword">function</span> (<span class="hljs-params">socket</span>) {<br> <span class="hljs-comment">// 创建一个连接后的实例,用于收发数据,错误处理等</span><br> <span class="hljs-comment">// 看下面的介绍</span><br><span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Connection</span>(socket, that, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br>that.<span class="hljs-property">connections</span>.<span class="hljs-title function_">push</span>(conn)<br>conn.<span class="hljs-title function_">removeListener</span>(<span class="hljs-string">'error'</span>, nop)<br>that.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'connection'</span>, conn)<br>})<br> <span class="hljs-comment">// 连接关闭后移除当前链接</span><br>conn.<span class="hljs-title function_">on</span>(<span class="hljs-string">'close'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><span class="hljs-keyword">var</span> pos = that.<span class="hljs-property">connections</span>.<span class="hljs-title function_">indexOf</span>(conn)<br><span class="hljs-keyword">if</span> (pos !== -<span class="hljs-number">1</span>) {<br>that.<span class="hljs-property">connections</span>.<span class="hljs-title function_">splice</span>(pos, <span class="hljs-number">1</span>)<br>}<br>})<br><br><span class="hljs-comment">// Ignore errors before the connection is established</span><br>conn.<span class="hljs-title function_">on</span>(<span class="hljs-string">'error'</span>, nop)<br>}<br> <span class="hljs-comment">// 创建一个内部的server实例</span><br> <span class="hljs-keyword">if</span> (secure) {<br> <span class="hljs-comment">// 创建wss服务</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span> = tls.<span class="hljs-title function_">createServer</span>(options, onConnection)<br>} <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 创建ws服务</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span> = net.<span class="hljs-title function_">createServer</span>(options, onConnection)<br>}<br> <span class="hljs-comment">// 保存已连接的web socket</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">connections</span> = []<br><br><span class="hljs-comment">// 继承时this绑定</span><br>events.<span class="hljs-property">EventEmitter</span>.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>)<br><span class="hljs-keyword">if</span> (callback) {<br> <span class="hljs-comment">// 当web socket 链接后调用</span><br> <span class="hljs-comment">// connection事件是自己实现的</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">on</span>(<span class="hljs-string">'connection'</span>, callback)<br>}<br>}<br><span class="hljs-comment">// 继承events.EventEmitter</span><br>util.<span class="hljs-title function_">inherits</span>(<span class="hljs-title class_">Server</span>, events.<span class="hljs-property">EventEmitter</span>)<br></code></pre></td></tr></table></figure><p><code>server.listen()</code> 用于开启<code>Web Socket</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><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><code class="hljs javascript"><span class="hljs-title class_">Server</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">listen</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">port, host, callback</span>) {<br><span class="hljs-keyword">if</span> (callback) {<br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">on</span>(<span class="hljs-string">'listening'</span>, callback)<br>}<br> <span class="hljs-comment">// 开启服务,此时可以通过连接访问了</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span>.<span class="hljs-title function_">listen</span>(port, host, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br>that.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'listening'</span>)<br>})<br><br><span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span><br>}<br><br></code></pre></td></tr></table></figure><p><code>Connection</code>类,是整个模块的核心,用于处理<code>Web Socket</code>的“握手”,以及“握手”成功后对数据的处理和异常的处理</p><blockquote><p>只看被作为服务端来使用时的处理</p></blockquote><p><code>Connection</code>构造函数,数据初始化,绑定<code>net.Socket</code>的<code>readable</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><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><code class="hljs javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">Connection</span>(<span class="hljs-params">socket, parentOrOptions, callback</span>) {<br><span class="hljs-keyword">var</span> that = <span class="hljs-variable language_">this</span>,<br>connectEvent<br> <span class="hljs-comment">// 初始化</span><br> <span class="hljs-comment">// Server-side connection</span><br> <span class="hljs-comment">// Server 实例</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">server</span> = parentOrOptions<br> <span class="hljs-comment">// websocket 的url</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">path</span> = <span class="hljs-literal">null</span><br> <span class="hljs-comment">// websocket 的host</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">host</span> = <span class="hljs-literal">null</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">extraHeaders</span> = <span class="hljs-literal">null</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">protocols</span> = []<br><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">protocol</span> = <span class="hljs-literal">undefined</span><br> <span class="hljs-comment">// net.Socket 的实例</span><br> <span class="hljs-comment">// https://nodejs.org/dist/latest-v18.x/docs/api/net.html#event-connection</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span> = socket<br> <span class="hljs-comment">// 连接状态</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">CONNECTING</span><br> <span class="hljs-comment">// 已接收数据</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span> = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">alloc</span>(<span class="hljs-number">0</span>)<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> = <span class="hljs-literal">null</span> <span class="hljs-comment">// string for text frames and InStream for binary frames</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">outStream</span> = <span class="hljs-literal">null</span> <span class="hljs-comment">// current allocated OutStream object for sending binary frames</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">key</span> = <span class="hljs-literal">null</span> <span class="hljs-comment">// the Sec-WebSocket-Key header</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span> = {} <span class="hljs-comment">// read only map of header names and values. Header names are lower-cased</span><br><br><span class="hljs-comment">// 入口,在这里读数据,包括“握手”和后面接收数据</span><br>socket.<span class="hljs-title function_">on</span>(<span class="hljs-string">'readable'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br>that.<span class="hljs-title function_">doRead</span>()<br>})<br> <span class="hljs-comment">// 错误处理</span><br>socket.<span class="hljs-title function_">on</span>(<span class="hljs-string">'error'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">err</span>) {<br>that.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'error'</span>, err)<br>})<br><br><span class="hljs-comment">// Close listeners</span><br> <span class="hljs-comment">// 连接关闭,状态重置</span><br><span class="hljs-keyword">var</span> onclose = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><span class="hljs-keyword">if</span> (that.<span class="hljs-property">readyState</span> === that.<span class="hljs-property">CONNECTING</span> || that.<span class="hljs-property">readyState</span> === that.<span class="hljs-property">OPEN</span>) {<br>that.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'close'</span>, <span class="hljs-number">1006</span>, <span class="hljs-string">''</span>)<br>}<br>that.<span class="hljs-property">readyState</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">CLOSED</span><br><span class="hljs-keyword">if</span> (that.<span class="hljs-property">frameBuffer</span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">InStream</span>) {<br>that.<span class="hljs-property">frameBuffer</span>.<span class="hljs-title function_">end</span>()<br>that.<span class="hljs-property">frameBuffer</span> = <span class="hljs-literal">null</span><br>}<br><span class="hljs-keyword">if</span> (that.<span class="hljs-property">outStream</span> <span class="hljs-keyword">instanceof</span> <span class="hljs-title class_">OutStream</span>) {<br>that.<span class="hljs-property">outStream</span>.<span class="hljs-title function_">end</span>()<br>that.<span class="hljs-property">outStream</span> = <span class="hljs-literal">null</span><br>}<br>}<br>socket.<span class="hljs-title function_">once</span>(<span class="hljs-string">'close'</span>, onclose)<br>socket.<span class="hljs-title function_">once</span>(<span class="hljs-string">'finish'</span>, onclose)<br><br><span class="hljs-comment">// 继承时this绑定</span><br>events.<span class="hljs-property">EventEmitter</span>.<span class="hljs-title function_">call</span>(<span class="hljs-variable language_">this</span>)<br><span class="hljs-keyword">if</span> (callback) {<br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">once</span>(<span class="hljs-string">'connect'</span>, callback)<br>}<br>}<br><span class="hljs-comment">// 继承events.EventEmitter</span><br>util.<span class="hljs-title function_">inherits</span>(<span class="hljs-title class_">Connection</span>, events.<span class="hljs-property">EventEmitter</span>)<br><br></code></pre></td></tr></table></figure><p><code>Connection.prototype.doRead()</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title class_">Connection</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">doRead</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><span class="hljs-keyword">var</span> buffer, temp<br><br><span class="hljs-comment">// Fetches the data</span><br> <span class="hljs-comment">// 从内部缓存区读取数据</span><br>buffer = <span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span>.<span class="hljs-title function_">read</span>()<br><span class="hljs-keyword">if</span> (!buffer) {<br><span class="hljs-comment">// Waits for more data</span><br><span class="hljs-keyword">return</span><br>}<br><br><span class="hljs-comment">// Save to the internal buffer</span><br> <span class="hljs-comment">// 保存当前进来的数据</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span> = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">concat</span>([<span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>, buffer], <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-property">length</span> + buffer.<span class="hljs-property">length</span>)<br> <span class="hljs-comment">// 如果连接状态为正在连接</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> === <span class="hljs-variable language_">this</span>.<span class="hljs-property">CONNECTING</span>) {<br> <span class="hljs-comment">// 握手处理</span><br><span class="hljs-keyword">if</span> (!<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">readHandshake</span>()) {<br><span class="hljs-comment">// May have failed or we're waiting for more data</span><br><span class="hljs-keyword">return</span><br>}<br>}<br><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> !== <span class="hljs-variable language_">this</span>.<span class="hljs-property">CLOSED</span>) {<br><span class="hljs-comment">// 读取数据</span><br><span class="hljs-keyword">while</span> ((temp = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">extractFrame</span>()) === <span class="hljs-literal">true</span>) {}<br><span class="hljs-keyword">if</span> (temp === <span class="hljs-literal">false</span>) {<br><span class="hljs-comment">// Protocol error</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">close</span>(<span class="hljs-number">1002</span>)<br><span class="hljs-comment">// 读取数据超过最大长度限制处理</span><br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-property">length</span> > <span class="hljs-title class_">Connection</span>.<span class="hljs-property">maxBufferLength</span>) {<br><span class="hljs-comment">// Frame too big</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">close</span>(<span class="hljs-number">1009</span>)<br>}<br>}<br>}<br></code></pre></td></tr></table></figure><p><code>Connection.prototype.readHandshake()</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title class_">Connection</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">readHandshake</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><span class="hljs-keyword">var</span> found = <span class="hljs-literal">false</span>,<br><br><span class="hljs-comment">// Search for '\r\n\r\n'</span><br><span class="hljs-comment">// 这里不是很理解,可能是搜索到这几个字符,则说明当前请求接收完成</span><br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-property">length</span> - <span class="hljs-number">3</span>; i++) {<br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>[i] === <span class="hljs-number">13</span> && <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>[i + <span class="hljs-number">2</span>] === <span class="hljs-number">13</span> &&<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>[i + <span class="hljs-number">1</span>] === <span class="hljs-number">10</span> && <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>[i + <span class="hljs-number">3</span>] === <span class="hljs-number">10</span>) {<br>found = <span class="hljs-literal">true</span><br><span class="hljs-keyword">break</span><br>}<br>}<br><span class="hljs-keyword">if</span> (!found) {<br><span class="hljs-comment">// Wait for more data</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-comment">// 拆分请求报文</span><br>data = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-title function_">slice</span>(<span class="hljs-number">0</span>, i + <span class="hljs-number">4</span>).<span class="hljs-title function_">toString</span>().<span class="hljs-title function_">split</span>(<span class="hljs-string">'\r\n'</span>)<br><span class="hljs-comment">// 切换Web Socket协议处理</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">answerHandshake</span>(data)) {<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-title function_">slice</span>(i + <span class="hljs-number">4</span>)<br><span class="hljs-comment">// 更改链接状态为open</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">OPEN</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'connect'</span>)<br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>} <span class="hljs-keyword">else</span> {<br><span class="hljs-comment">// 对于Web Socket的错误处理都是返回这个响应信息</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span>.<span class="hljs-title function_">end</span>(<span class="hljs-string">'HTTP/1.1 400 Bad Request\r\n\r\n'</span>)<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br>}<br></code></pre></td></tr></table></figure><p><code>Connection.prototype.answerHandshake()</code>将<code>HTTP</code>协议切换成<code>Web Socket</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><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title class_">Connection</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">answerHandshake</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">lines</span>) {<br><span class="hljs-keyword">var</span> path, key, sha1, headers<br><br><span class="hljs-comment">// 请求报文,必定包含上面(`工作方式所列出的内容`)提到的内容</span><br><span class="hljs-keyword">if</span> (lines.<span class="hljs-property">length</span> < <span class="hljs-number">6</span>) {<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-comment">// 解析请求url</span><br>path = lines[<span class="hljs-number">0</span>].<span class="hljs-title function_">match</span>(<span class="hljs-regexp">/^GET (.+) HTTP\/\d\.\d$/i</span>)<br><span class="hljs-keyword">if</span> (!path) {<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">path</span> = path[<span class="hljs-number">1</span>]<br><br><span class="hljs-comment">// 解析为正确的headers格式,代码在下面</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">readHeaders</span>(lines)<br><br><span class="hljs-comment">// 验证必要的header</span><br><span class="hljs-keyword">if</span> (!(<span class="hljs-string">'host'</span> <span class="hljs-keyword">in</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>) ||<br>!(<span class="hljs-string">'sec-websocket-key'</span> <span class="hljs-keyword">in</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>) ||<br>!(<span class="hljs-string">'upgrade'</span> <span class="hljs-keyword">in</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>) ||<br>!(<span class="hljs-string">'connection'</span> <span class="hljs-keyword">in</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>)) {<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-comment">// 'Upgrade' 的值必须是'websocket'</span><br><span class="hljs-comment">// 'Connection' 的值必须存在'Upgrade'</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>.<span class="hljs-property">upgrade</span>.<span class="hljs-title function_">toLowerCase</span>() !== <span class="hljs-string">'websocket'</span> ||<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>.<span class="hljs-property">connection</span>.<span class="hljs-title function_">toLowerCase</span>().<span class="hljs-title function_">split</span>(<span class="hljs-regexp">/\s*,\s*/</span>).<span class="hljs-title function_">indexOf</span>(<span class="hljs-string">'upgrade'</span>) === -<span class="hljs-number">1</span>) {<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-comment">// 决定当前链接的Web Scoket协议的版本</span><br><span class="hljs-comment">// 这里对不符合的版本,在响应报文中,没有做提示,而是直接当成错误来处理</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>[<span class="hljs-string">'sec-websocket-version'</span>] !== <span class="hljs-string">'13'</span>) {<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">key</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>[<span class="hljs-string">'sec-websocket-key'</span>]<br><br><span class="hljs-comment">// Agree on a protocol</span><br><span class="hljs-keyword">if</span> (<span class="hljs-string">'sec-websocket-protocol'</span> <span class="hljs-keyword">in</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>) {<br><span class="hljs-comment">// Parse</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">protocols</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>[<span class="hljs-string">'sec-websocket-protocol'</span>].<span class="hljs-title function_">split</span>(<span class="hljs-string">','</span>).<span class="hljs-title function_">map</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">each</span>) {<br><span class="hljs-keyword">return</span> each.<span class="hljs-title function_">trim</span>()<br>})<br><br><span class="hljs-comment">// Select protocol</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">server</span>.<span class="hljs-property">_selectProtocol</span>) {<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">protocol</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">server</span>.<span class="hljs-title function_">_selectProtocol</span>(<span class="hljs-variable language_">this</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">protocols</span>)<br>}<br>}<br><br><span class="hljs-comment">// Build and send the response</span><br><span class="hljs-comment">// 生成一个`Sec-WebSocket-Accept` key</span><br>sha1 = crypto.<span class="hljs-title function_">createHash</span>(<span class="hljs-string">'sha1'</span>)<br>sha1.<span class="hljs-title function_">end</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">key</span> + <span class="hljs-string">'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'</span>)<br>key = sha1.<span class="hljs-title function_">read</span>().<span class="hljs-title function_">toString</span>(<span class="hljs-string">'base64'</span>)<br><br><span class="hljs-comment">// 响应头</span><br>headers = {<br><span class="hljs-title class_">Upgrade</span>: <span class="hljs-string">'websocket'</span>,<br><span class="hljs-title class_">Connection</span>: <span class="hljs-string">'Upgrade'</span>,<br><span class="hljs-string">'Sec-WebSocket-Accept'</span>: key<br>}<br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">protocol</span>) {<br>headers[<span class="hljs-string">'Sec-WebSocket-Protocol'</span>] = <span class="hljs-variable language_">this</span>.<span class="hljs-property">protocol</span><br>}<br><br><span class="hljs-comment">// 切换为Web Socket协议的应答</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span>.<span class="hljs-title function_">write</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">buildRequest</span>(<span class="hljs-string">'HTTP/1.1 101 Switching Protocols'</span>, headers))<br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>}<br><br><span class="hljs-comment">// 将请求头解析成对象形式的headers</span><br><span class="hljs-title class_">Connection</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">readHeaders</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">lines</span>) {<br><span class="hljs-keyword">var</span> i, match<br><br><span class="hljs-comment">// Extract all headers</span><br><span class="hljs-comment">// Ignore bad-formed lines and ignore the first line (HTTP header)</span><br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i < lines.<span class="hljs-property">length</span>; i++) {<br><span class="hljs-keyword">if</span> ((match = lines[i].<span class="hljs-title function_">match</span>(<span class="hljs-regexp">/^([a-z-]+): (.+)$/i</span>))) {<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">headers</span>[match[<span class="hljs-number">1</span>].<span class="hljs-title function_">toLowerCase</span>()] = match[<span class="hljs-number">2</span>]<br>}<br>}<br>}<br></code></pre></td></tr></table></figure><p>以上就完成了连接的握手处理</p><h3 id="数据解析"><a href="#数据解析" class="headerlink" title="数据解析"></a>数据解析</h3><p><code>WebSocket</code> 以 <code>frame</code> 为单位传输数据, <code>frame</code> 是客户端和服务端数据传输的最小单元, 当一条消息过长时, 通信方可以将该消息拆分成多个 <code>frame</code> 发送, 接收方收到以后重新拼接、解码从而还原出完整的消息, 在 <code>WebSocket</code> 中, <code>frame</code> 有多种类型, <code>frame</code> 的类型由 <code>frame</code> 头部的 <code>Opcode</code> 字段指示</p><p><code>Connection.prototype.extractFrame()</code> 从缓冲区中提取帧(<code>frame</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><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title class_">Connection</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">extractFrame</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br><span class="hljs-keyword">var</span> fin, opcode, B, <span class="hljs-variable constant_">HB</span>, mask, len, payload, start, i, hasMask<br><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-property">length</span> < <span class="hljs-number">2</span>) {<br><span class="hljs-keyword">return</span><br>}<br><br><span class="hljs-comment">// Is this the last frame in a sequence?</span><br>B = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>[<span class="hljs-number">0</span>]<br><span class="hljs-variable constant_">HB</span> = B >> <span class="hljs-number">4</span><br><span class="hljs-comment">// 判断RSV1, RSV2, RSV3位组成的自定义协议</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable constant_">HB</span> % <span class="hljs-number">8</span>) {<br><span class="hljs-comment">// RSV1, RSV2 and RSV3 must be clear</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-comment">// 用于指示当前的 frame 是消息的最后一个分段,1为最后一个frame</span><br>fin = <span class="hljs-variable constant_">HB</span> === <span class="hljs-number">8</span><br><span class="hljs-comment">// 操作码</span><br><span class="hljs-comment">// * %x0 表示连续消息片断</span><br><span class="hljs-comment">// * %x1 表示文本消息片断</span><br><span class="hljs-comment">// * %x2 表未二进制消息片断</span><br><span class="hljs-comment">// * %x3-7 为将来的非控制消息片断保留的操作码</span><br><span class="hljs-comment">// * %x8 表示连接关闭</span><br><span class="hljs-comment">// * %x9 表示心跳检查的ping</span><br><span class="hljs-comment">// * %xA 表示心跳检查的pong</span><br><span class="hljs-comment">// * %xB-F 为将来的控制消息片断的保留操作码</span><br>opcode = B % <span class="hljs-number">16</span><br><span class="hljs-comment">// 判断是否是有效操作码,0~F,除了这里列出的,其他都为保留的操作码</span><br><span class="hljs-keyword">if</span> (opcode !== <span class="hljs-number">0</span> && opcode !== <span class="hljs-number">1</span> && opcode !== <span class="hljs-number">2</span> &&<br>opcode !== <span class="hljs-number">8</span> && opcode !== <span class="hljs-number">9</span> && opcode !== <span class="hljs-number">10</span>) {<br><span class="hljs-comment">// Invalid opcode</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-keyword">if</span> (opcode >= <span class="hljs-number">8</span> && !fin) {<br><span class="hljs-comment">// Control frames must not be fragmented</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><br>B = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>[<span class="hljs-number">1</span>]<br><span class="hljs-comment">// 取出Mask位,判断传输的数据是否有加掩码,设置为1,掩码键必须放在Masking-key区域</span><br><span class="hljs-comment">// 客户端发送给服务端的所有消息,此位的值都是1;</span><br>hasMask = B >> <span class="hljs-number">7</span><br><span class="hljs-keyword">if</span> ((<span class="hljs-variable language_">this</span>.<span class="hljs-property">server</span> && !hasMask) || (!<span class="hljs-variable language_">this</span>.<span class="hljs-property">server</span> && hasMask)) {<br><span class="hljs-comment">// Frames sent by clients must be masked</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><br><span class="hljs-comment">// Payload length处理</span><br><span class="hljs-comment">// 获取 Payload length:传输数据的长度</span><br><span class="hljs-comment">// 如果这个值以字节表示是0-125这个范围,那这个值就表示传输数据的长度;</span><br><span class="hljs-comment">// 如果这个值是126,则随后的两个字节表示的是一个16进制无符号数,用来表示传输数据的长度;</span><br><span class="hljs-comment">// 如果这个值是127,则随后的是8个字节表示的一个64位无符合数,这个数用来表示传输数据的长度</span><br>len = B % <span class="hljs-number">128</span><br>start = hasMask ? <span class="hljs-number">6</span> : <span class="hljs-number">2</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-property">length</span> < start + len) {<br><span class="hljs-comment">// Not enough data in the buffer</span><br><span class="hljs-keyword">return</span><br>}<br><span class="hljs-comment">// 获取实际的 payload length</span><br><span class="hljs-keyword">if</span> (len === <span class="hljs-number">126</span>) {<br>len = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-title function_">readUInt16BE</span>(<span class="hljs-number">2</span>)<br>start += <span class="hljs-number">2</span><br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (len === <span class="hljs-number">127</span>) {<br><span class="hljs-comment">// Warning: JS can only store up to 2^53 in its number format</span><br>len = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-title function_">readUInt32BE</span>(<span class="hljs-number">2</span>) * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">pow</span>(<span class="hljs-number">2</span>, <span class="hljs-number">32</span>) + <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-title function_">readUInt32BE</span>(<span class="hljs-number">6</span>)<br>start += <span class="hljs-number">8</span><br>}<br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-property">length</span> < start + len) {<br><span class="hljs-keyword">return</span><br>}<br><br><span class="hljs-comment">// 提取有效数据</span><br>payload = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-title function_">slice</span>(start, start + len)<br><span class="hljs-comment">// 如果有Mask位,使用给定掩码解码</span><br><span class="hljs-keyword">if</span> (hasMask) {<br><span class="hljs-comment">// Decode with the given mask</span><br>mask = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-title function_">slice</span>(start - <span class="hljs-number">4</span>, start)<br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < payload.<span class="hljs-property">length</span>; i++) {<br>payload[i] ^= mask[i % <span class="hljs-number">4</span>]<br>}<br>}<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">buffer</span>.<span class="hljs-title function_">slice</span>(start + len)<br><br><span class="hljs-comment">// Proceeds to frame processing</span><br><span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">processFrame</span>(fin, opcode, payload)<br>}<br></code></pre></td></tr></table></figure><p><code>Connection.prototype.processFrame()</code> 处理接收到的给定帧(<code>frame</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><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title class_">Connection</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">processFrame</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">fin, opcode, payload</span>) {<br><span class="hljs-comment">// 链接关闭处理</span><br><span class="hljs-keyword">if</span> (opcode === <span class="hljs-number">8</span>) {<br><span class="hljs-comment">// Close frame</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> === <span class="hljs-variable language_">this</span>.<span class="hljs-property">CLOSING</span>) {<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span>.<span class="hljs-title function_">end</span>()<br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> === <span class="hljs-variable language_">this</span>.<span class="hljs-property">OPEN</span>) {<br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">processCloseFrame</span>(payload)<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>} <br><span class="hljs-comment">// 心跳检查的ping 处理</span><br><span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (opcode === <span class="hljs-number">9</span>) {<br><span class="hljs-comment">// Ping frame</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> === <span class="hljs-variable language_">this</span>.<span class="hljs-property">OPEN</span>) {<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span>.<span class="hljs-title function_">write</span>(frame.<span class="hljs-title function_">createPongFrame</span>(payload.<span class="hljs-title function_">toString</span>(), !<span class="hljs-variable language_">this</span>.<span class="hljs-property">server</span>))<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>} <br><span class="hljs-comment">// 心跳检查的pong 处理</span><br><span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (opcode === <span class="hljs-number">10</span>) {<br><span class="hljs-comment">// Pong frame</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'pong'</span>, payload.<span class="hljs-title function_">toString</span>())<br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>}<br><br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> !== <span class="hljs-variable language_">this</span>.<span class="hljs-property">OPEN</span>) {<br><span class="hljs-comment">// Ignores if the connection isn't opened anymore</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>}<br><span class="hljs-comment">// 连续消息帧处理</span><br><span class="hljs-keyword">if</span> (opcode === <span class="hljs-number">0</span> && <span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> === <span class="hljs-literal">null</span>) {<br><span class="hljs-comment">// Unexpected continuation frame</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (opcode !== <span class="hljs-number">0</span> && <span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> !== <span class="hljs-literal">null</span>) {<br><span class="hljs-comment">// Last sequence didn't finished correctly</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><br><span class="hljs-keyword">if</span> (!opcode) {<br><span class="hljs-comment">// Get the current opcode for fragmented frames</span><br>opcode = <span class="hljs-keyword">typeof</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> === <span class="hljs-string">'string'</span> ? <span class="hljs-number">1</span> : <span class="hljs-number">2</span><br>}<br><span class="hljs-comment">// 文本消息帧处理</span><br><span class="hljs-keyword">if</span> (opcode === <span class="hljs-number">1</span>) {<br><span class="hljs-comment">// Save text frame</span><br>payload = payload.<span class="hljs-title function_">toString</span>()<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> ? <span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> + payload : payload<br><br><span class="hljs-keyword">if</span> (fin) {<br><span class="hljs-comment">// Emits 'text' event</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'text'</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span>)<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> = <span class="hljs-literal">null</span><br>}<br>} <br><span class="hljs-comment">// 二进制消息帧处理</span><br><span class="hljs-keyword">else</span> {<br><span class="hljs-comment">// Sends the buffer for InStream object</span><br><span class="hljs-keyword">if</span> (!<span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span>) {<br><span class="hljs-comment">// Emits the 'binary' event</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">InStream</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'binary'</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span>)<br>}<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span>.<span class="hljs-title function_">addData</span>(payload)<br><br><span class="hljs-keyword">if</span> (fin) {<br><span class="hljs-comment">// Emits 'end' event</span><br><span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span>.<span class="hljs-title function_">end</span>()<br><span class="hljs-variable language_">this</span>.<span class="hljs-property">frameBuffer</span> = <span class="hljs-literal">null</span><br>}<br>}<br><br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>}<br></code></pre></td></tr></table></figure><h3 id="数据发送"><a href="#数据发送" class="headerlink" title="数据发送"></a>数据发送</h3><p>发送数据,需要创建与接收时一样的数据结构,包括<code>fin, opcode, mask, payload-length, mask-key</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><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title class_">Connection</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">sendText</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">str, callback</span>) {<br><span class="hljs-keyword">if</span> (<span class="hljs-variable language_">this</span>.<span class="hljs-property">readyState</span> === <span class="hljs-variable language_">this</span>.<span class="hljs-property">OPEN</span>) {<br><span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">socket</span>.<span class="hljs-title function_">write</span>(frame.<span class="hljs-title function_">createTextFrame</span>(str, !<span class="hljs-variable language_">this</span>.<span class="hljs-property">server</span>), callback)<br>}<br>}<br><span class="hljs-comment">// 创建一个文本frame</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">createTextFrame</span>(<span class="hljs-params">data, masked</span>) {<br><span class="hljs-keyword">var</span> payload, meta<br><br>payload = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">from</span>(data)<br>meta = <span class="hljs-title function_">generateMetaData</span>(<span class="hljs-literal">true</span>, <span class="hljs-number">1</span>, masked === <span class="hljs-literal">undefined</span> ? <span class="hljs-literal">false</span> : masked, payload)<br><br><span class="hljs-keyword">return</span> <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">concat</span>([meta, payload], meta.<span class="hljs-property">length</span> + payload.<span class="hljs-property">length</span>)<br>}<br><span class="hljs-comment">// 创建frame的元数据部分</span><br><span class="hljs-comment">// 如果frame被屏蔽了,则payload会相应地改变</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">generateMetaData</span>(<span class="hljs-params">fin, opcode, masked, payload</span>) {<br><span class="hljs-keyword">var</span> len, meta, start, mask, i<br><br>len = payload.<span class="hljs-property">length</span><br><br><span class="hljs-comment">// 为元数据创建缓冲区</span><br>meta = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">alloc</span>(<span class="hljs-number">2</span> + (len < <span class="hljs-number">126</span> ? <span class="hljs-number">0</span> : (len < <span class="hljs-number">65536</span> ? <span class="hljs-number">2</span> : <span class="hljs-number">8</span>)) + (masked ? <span class="hljs-number">4</span> : <span class="hljs-number">0</span>))<br><br><span class="hljs-comment">// 设置 fin 和 opcode</span><br>meta[<span class="hljs-number">0</span>] = (fin ? <span class="hljs-number">128</span> : <span class="hljs-number">0</span>) + opcode<br><br><span class="hljs-comment">// 设置 Mask 和 length</span><br>meta[<span class="hljs-number">1</span>] = masked ? <span class="hljs-number">128</span> : <span class="hljs-number">0</span><br>start = <span class="hljs-number">2</span><br><span class="hljs-keyword">if</span> (len < <span class="hljs-number">126</span>) {<br>meta[<span class="hljs-number">1</span>] += len<br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (len < <span class="hljs-number">65536</span>) {<br>meta[<span class="hljs-number">1</span>] += <span class="hljs-number">126</span><br>meta.<span class="hljs-title function_">writeUInt16BE</span>(len, <span class="hljs-number">2</span>)<br>start += <span class="hljs-number">2</span><br>} <span class="hljs-keyword">else</span> {<br><span class="hljs-comment">// Warning: JS doesn't support integers greater than 2^53</span><br>meta[<span class="hljs-number">1</span>] += <span class="hljs-number">127</span><br>meta.<span class="hljs-title function_">writeUInt32BE</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(len / <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">pow</span>(<span class="hljs-number">2</span>, <span class="hljs-number">32</span>)), <span class="hljs-number">2</span>)<br>meta.<span class="hljs-title function_">writeUInt32BE</span>(len % <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">pow</span>(<span class="hljs-number">2</span>, <span class="hljs-number">32</span>), <span class="hljs-number">6</span>)<br>start += <span class="hljs-number">8</span><br>}<br><br><span class="hljs-comment">// 设置mask-key</span><br><span class="hljs-keyword">if</span> (masked) {<br>mask = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">alloc</span>(<span class="hljs-number">4</span>)<br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < <span class="hljs-number">4</span>; i++) {<br>meta[start + i] = mask[i] = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * <span class="hljs-number">256</span>)<br>}<br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < payload.<span class="hljs-property">length</span>; i++) {<br>payload[i] ^= mask[i % <span class="hljs-number">4</span>]<br>}<br>start += <span class="hljs-number">4</span><br>}<br><br><span class="hljs-keyword">return</span> meta<br>}<br></code></pre></td></tr></table></figure><h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><p>接下来我们就可以根据上面的逻辑,实现一个简单版的websocket服务,包括握手和数据收发功能</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><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> net = <span class="hljs-built_in">require</span>(<span class="hljs-string">"net"</span>)<br><span class="hljs-keyword">const</span> crypto = <span class="hljs-built_in">require</span>(<span class="hljs-string">'crypto'</span>)<br><br><span class="hljs-comment">// 链接状态类别</span><br><span class="hljs-keyword">const</span> <span class="hljs-variable constant_">CONNECTING</span> = <span class="hljs-number">0</span>;<br><span class="hljs-keyword">const</span> <span class="hljs-variable constant_">OPEN</span> = <span class="hljs-number">1</span>;<br><br><span class="hljs-comment">// 默认状态</span><br><span class="hljs-keyword">let</span> readyState = <span class="hljs-variable constant_">CONNECTING</span><br><br><span class="hljs-comment">// websocket 入口</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">myWebsocket</span> = (<span class="hljs-params">{port, callback}</span>) => {<br> <span class="hljs-keyword">const</span> server = <span class="hljs-title function_">createServer</span>(callback)<br> server.<span class="hljs-title function_">listen</span>(port)<br>}<br><br><span class="hljs-comment">// 创建server</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">createServer</span> = (<span class="hljs-params">callback</span>) => {<br> <span class="hljs-keyword">let</span> buffer<br> <span class="hljs-keyword">const</span> server = net.<span class="hljs-title function_">createServer</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">socket</span>) {<br><span class="hljs-comment">// 监听 "readable" 事件,从而获取请求的数据包</span><br> socket.<span class="hljs-title function_">on</span>(<span class="hljs-string">"readable"</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'readable....'</span>);<br> <span class="hljs-title function_">doRead</span>(socket, callback)<br> })<br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">close</span> = (<span class="hljs-params"></span>) => {<br> readyState = <span class="hljs-variable constant_">CONNECTING</span><br> }<br> socket.<span class="hljs-title function_">on</span>(<span class="hljs-string">'close'</span>, close)<br> socket.<span class="hljs-title function_">on</span>(<span class="hljs-string">'error'</span>, close)<br> })<br><br> <span class="hljs-keyword">return</span> server<br>}<br><br><span class="hljs-comment">// 数据解析</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">doRead</span> = (<span class="hljs-params">socket, callback</span>) => {<br> <span class="hljs-keyword">let</span> buffer, headers<br><span class="hljs-comment">// 从缓冲区获取请求的数据</span><br> buffer = socket.<span class="hljs-title function_">read</span>()<br><br><span class="hljs-comment">// 握手处理</span><br> <span class="hljs-keyword">if</span> (buffer && readyState === <span class="hljs-variable constant_">CONNECTING</span>) {<br> <span class="hljs-keyword">const</span> lines = buffer.<span class="hljs-title function_">toString</span>().<span class="hljs-title function_">split</span>(<span class="hljs-string">'\r\n'</span>)<br> <span class="hljs-keyword">const</span> path = lines[<span class="hljs-number">0</span>].<span class="hljs-title function_">match</span>(<span class="hljs-regexp">/^GET (.*) HTTP\/1.1$/</span>)<br> <span class="hljs-keyword">if</span> (path) {<br> <span class="hljs-comment">// console.log(lines);</span><br> headers = <span class="hljs-title function_">readHeaders</span>(lines.<span class="hljs-title function_">slice</span>(<span class="hljs-number">1</span>))<br> <span class="hljs-keyword">if</span> (!<span class="hljs-title function_">checkHeaders</span>(headers)) {<br> <span class="hljs-keyword">return</span> socket.<span class="hljs-title function_">end</span>(<span class="hljs-string">"HTTP/1.1 400 Switching Protocols \r\n\r\n"</span>);<br> }<br><br> readyState = <span class="hljs-variable constant_">OPEN</span>;<br> socket.<span class="hljs-title function_">write</span>(<span class="hljs-string">"HTTP/1.1 101 Switching Protocols\r\n"</span> + <span class="hljs-title function_">returnHeaders</span>(headers))<br> } <span class="hljs-keyword">else</span> {<br> <br> <span class="hljs-keyword">return</span>;<br> }<br> } <span class="hljs-keyword">else</span> {<br><span class="hljs-comment">// websocket 连接后解析数据</span><br> <span class="hljs-keyword">if</span> (readyState === <span class="hljs-variable constant_">OPEN</span>) {<br> <span class="hljs-keyword">const</span> payload = <span class="hljs-title function_">parseData</span>(buffer)<br><span class="hljs-comment">// 用户传递的回调</span><br> callback&&<span class="hljs-title function_">callback</span>(payload.<span class="hljs-title function_">toString</span>(), <span class="hljs-title function_">sendText</span>(socket))<br> }<br> }<br>}<br><span class="hljs-comment">// 获取headers</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">readHeaders</span> = (<span class="hljs-params">lines</span>) => {<br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">fromEntries</span>(lines.<span class="hljs-title function_">filter</span>(<span class="hljs-function"><span class="hljs-params">line</span> =></span> !!line).<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">line</span> =></span> {<br> <span class="hljs-keyword">const</span> v = line.<span class="hljs-title function_">match</span>(<span class="hljs-regexp">/^([a-zA-Z-]+): (.+)$/</span>)<br> <span class="hljs-keyword">return</span> [v[<span class="hljs-number">1</span>].<span class="hljs-title function_">toLowerCase</span>(), v[<span class="hljs-number">2</span>]]<br> }));<br>}<br><span class="hljs-comment">// 检查headers中是否符合websocket的请求头</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">checkHeaders</span> = (<span class="hljs-params">headers</span>) => {<br> <span class="hljs-keyword">if</span> (<span class="hljs-string">'connection'</span> <span class="hljs-keyword">in</span> headers && <span class="hljs-string">'upgrade'</span> <span class="hljs-keyword">in</span> headers && <span class="hljs-string">'sec-websocket-version'</span> <span class="hljs-keyword">in</span> headers && <span class="hljs-string">'sec-websocket-key'</span> <span class="hljs-keyword">in</span> headers) {<br><br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br> }<br><br> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-comment">// 切换协议时需要返回的请求头</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">returnHeaders</span> = (<span class="hljs-params">headers</span>) => {<br><span class="hljs-comment">// 需要使用sha1生成一个 Sec-WebSocket-Accept</span><br> <span class="hljs-keyword">const</span> sha1 = crypto.<span class="hljs-title function_">createHash</span>(<span class="hljs-string">'sha1'</span>)<br> <span class="hljs-keyword">const</span> key = sha1.<span class="hljs-title function_">end</span>(headers[<span class="hljs-string">'sec-websocket-key'</span>] + <span class="hljs-string">'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'</span>).<span class="hljs-title function_">read</span>().<span class="hljs-title function_">toString</span>(<span class="hljs-string">'base64'</span>)<br><br> <span class="hljs-keyword">return</span> <span class="hljs-title function_">buildRequest</span>({<br> <span class="hljs-title class_">Upgrade</span>: <span class="hljs-string">'websocket'</span>,<br><span class="hljs-title class_">Connection</span>: <span class="hljs-string">'Upgrade'</span>,<br><span class="hljs-string">'Sec-WebSocket-Accept'</span>: key<br> })<br>}<br><span class="hljs-comment">// 生成返回的请求头格式</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">buildRequest</span> = (<span class="hljs-params">headers</span>) => {<br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Object</span>.<span class="hljs-title function_">keys</span>(headers).<span class="hljs-title function_">reduce</span>(<span class="hljs-function">(<span class="hljs-params">collect, key</span>) =></span> {<br> collect += <span class="hljs-string">`<span class="hljs-subst">${key}</span>: <span class="hljs-subst">${headers[key]}</span>\r\n`</span><br><br> <span class="hljs-keyword">return</span> collect;<br> }, <span class="hljs-string">''</span>) + <span class="hljs-string">"\r\n"</span><br>}<br><span class="hljs-comment">// 发送文本数据</span><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">sendText</span> = socket => <span class="hljs-function"><span class="hljs-params">data</span> =></span> {<br><span class="hljs-keyword">const</span> payload = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">from</span>(data);<br><span class="hljs-keyword">const</span> meta = <span class="hljs-title function_">generateMetaData</span>(<span class="hljs-literal">true</span>, <span class="hljs-number">1</span>, <span class="hljs-literal">false</span>, payload)<br><br>socket.<span class="hljs-title function_">write</span>(<span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">concat</span>([meta, payload], meta.<span class="hljs-property">length</span> + payload.<span class="hljs-property">length</span>))<br>}<br><br><span class="hljs-comment">// 这部分逻辑是直接拿过来用的,还是别人的更香,注释就看上面</span><br><span class="hljs-keyword">const</span> parseData = <span class="hljs-keyword">function</span> (<span class="hljs-params">buffer</span>) {<br><span class="hljs-keyword">var</span> fin, opcode, B, <span class="hljs-variable constant_">HB</span>, mask, len, payload, start, i, hasMask<br><br><span class="hljs-keyword">if</span> (buffer.<span class="hljs-property">length</span> < <span class="hljs-number">2</span>) {<br><span class="hljs-keyword">return</span><br>}<br><br><span class="hljs-comment">// Is this the last frame in a sequence?</span><br>B = buffer[<span class="hljs-number">0</span>]<br><span class="hljs-variable constant_">HB</span> = B >> <span class="hljs-number">4</span><br><span class="hljs-keyword">if</span> (<span class="hljs-variable constant_">HB</span> % <span class="hljs-number">8</span>) {<br><span class="hljs-comment">// RSV1, RSV2 and RSV3 must be clear</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br>fin = <span class="hljs-variable constant_">HB</span> === <span class="hljs-number">8</span><br>opcode = B % <span class="hljs-number">16</span><br><br><span class="hljs-keyword">if</span> (opcode !== <span class="hljs-number">0</span> && opcode !== <span class="hljs-number">1</span> && opcode !== <span class="hljs-number">2</span> &&<br>opcode !== <span class="hljs-number">8</span> && opcode !== <span class="hljs-number">9</span> && opcode !== <span class="hljs-number">10</span>) {<br><span class="hljs-comment">// Invalid opcode</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><span class="hljs-keyword">if</span> (opcode >= <span class="hljs-number">8</span> && !fin) {<br><span class="hljs-comment">// Control frames must not be fragmented</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br><br>B = buffer[<span class="hljs-number">1</span>]<br>hasMask = B >> <span class="hljs-number">7</span><br><span class="hljs-keyword">if</span> (!hasMask) {<br><span class="hljs-comment">// Frames sent by clients must be masked</span><br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br>}<br>len = B % <span class="hljs-number">128</span><br>start = hasMask ? <span class="hljs-number">6</span> : <span class="hljs-number">2</span><br><br><span class="hljs-keyword">if</span> (buffer.<span class="hljs-property">length</span> < start + len) {<br><span class="hljs-comment">// Not enough data in the buffer</span><br><span class="hljs-keyword">return</span><br>}<br><br><span class="hljs-comment">// Get the actual payload length</span><br><span class="hljs-keyword">if</span> (len === <span class="hljs-number">126</span>) {<br>len = buffer.<span class="hljs-title function_">readUInt16BE</span>(<span class="hljs-number">2</span>)<br>start += <span class="hljs-number">2</span><br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (len === <span class="hljs-number">127</span>) {<br><span class="hljs-comment">// Warning: JS can only store up to 2^53 in its number format</span><br>len = buffer.<span class="hljs-title function_">readUInt32BE</span>(<span class="hljs-number">2</span>) * <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">pow</span>(<span class="hljs-number">2</span>, <span class="hljs-number">32</span>) + buffer.<span class="hljs-title function_">readUInt32BE</span>(<span class="hljs-number">6</span>)<br>start += <span class="hljs-number">8</span><br>}<br><span class="hljs-keyword">if</span> (buffer.<span class="hljs-property">length</span> < start + len) {<br><span class="hljs-keyword">return</span><br>}<br><br><span class="hljs-comment">// Extract the payload</span><br>payload = buffer.<span class="hljs-title function_">slice</span>(start, start + len)<br><span class="hljs-keyword">if</span> (hasMask) {<br><span class="hljs-comment">// Decode with the given mask</span><br>mask = buffer.<span class="hljs-title function_">slice</span>(start - <span class="hljs-number">4</span>, start)<br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < payload.<span class="hljs-property">length</span>; i++) {<br>payload[i] ^= mask[i % <span class="hljs-number">4</span>]<br>}<br>}<br>buffer = buffer.<span class="hljs-title function_">slice</span>(start + len)<br><br><span class="hljs-comment">// Proceeds to frame processing</span><br><span class="hljs-keyword">return</span> payload<br>}<br><span class="hljs-comment">// 这部分逻辑是直接拿过来用的,还是别人的更香,注释就看上面</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">generateMetaData</span>(<span class="hljs-params">fin, opcode, masked, payload</span>) {<br><span class="hljs-keyword">var</span> len, meta, start, mask, i<br><br>len = payload.<span class="hljs-property">length</span><br><br><span class="hljs-comment">// Creates the buffer for meta-data</span><br>meta = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">alloc</span>(<span class="hljs-number">2</span> + (len < <span class="hljs-number">126</span> ? <span class="hljs-number">0</span> : (len < <span class="hljs-number">65536</span> ? <span class="hljs-number">2</span> : <span class="hljs-number">8</span>)) + (masked ? <span class="hljs-number">4</span> : <span class="hljs-number">0</span>))<br><br><span class="hljs-comment">// Sets fin and opcode</span><br>meta[<span class="hljs-number">0</span>] = (fin ? <span class="hljs-number">128</span> : <span class="hljs-number">0</span>) + opcode<br><br><span class="hljs-comment">// Sets the mask and length</span><br>meta[<span class="hljs-number">1</span>] = masked ? <span class="hljs-number">128</span> : <span class="hljs-number">0</span><br>start = <span class="hljs-number">2</span><br><span class="hljs-keyword">if</span> (len < <span class="hljs-number">126</span>) {<br>meta[<span class="hljs-number">1</span>] += len<br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (len < <span class="hljs-number">65536</span>) {<br>meta[<span class="hljs-number">1</span>] += <span class="hljs-number">126</span><br>meta.<span class="hljs-title function_">writeUInt16BE</span>(len, <span class="hljs-number">2</span>)<br>start += <span class="hljs-number">2</span><br>} <span class="hljs-keyword">else</span> {<br><span class="hljs-comment">// Warning: JS doesn't support integers greater than 2^53</span><br>meta[<span class="hljs-number">1</span>] += <span class="hljs-number">127</span><br>meta.<span class="hljs-title function_">writeUInt32BE</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(len / <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">pow</span>(<span class="hljs-number">2</span>, <span class="hljs-number">32</span>)), <span class="hljs-number">2</span>)<br>meta.<span class="hljs-title function_">writeUInt32BE</span>(len % <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">pow</span>(<span class="hljs-number">2</span>, <span class="hljs-number">32</span>), <span class="hljs-number">6</span>)<br>start += <span class="hljs-number">8</span><br>}<br><br><span class="hljs-comment">// Set the mask-key</span><br><span class="hljs-keyword">if</span> (masked) {<br>mask = <span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">alloc</span>(<span class="hljs-number">4</span>)<br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < <span class="hljs-number">4</span>; i++) {<br>meta[start + i] = mask[i] = <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">floor</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * <span class="hljs-number">256</span>)<br>}<br><span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>; i < payload.<span class="hljs-property">length</span>; i++) {<br>payload[i] ^= mask[i % <span class="hljs-number">4</span>]<br>}<br>start += <span class="hljs-number">4</span><br>}<br><br><span class="hljs-keyword">return</span> meta<br>}<br><br><span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = myWebsocket<br></code></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-title function_">myWebsocket</span>({<span class="hljs-attr">port</span>: <span class="hljs-number">3000</span>, <span class="hljs-title function_">callback</span>(<span class="hljs-params">data, sendText</span>) {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Received Data: '</span> + data);<br> <span class="hljs-title function_">sendText</span>(data.<span class="hljs-title function_">toUpperCase</span>() + <span class="hljs-string">"!!!!"</span>);<br>}})<br></code></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>经过对<code>nodejs-websocket</code>源码的阅读,我们从深层次了解了<code>websocket</code>是如何进行握手,并且如何从HTTP协议切换到<code>Web Socket</code>协议,以及在对数据进行解析时,需要通过哪些标志位(<code>FIN, Opcode, Mask</code>等)来告诉我们当前数据的状态是怎么样的,我们该如何处理。然后在代码的实现中,我们可以更好的去学习<code>Nodejs</code>是如何搭建一个服务,并加深我们对<code>nodejs</code>的开发能力</p><p>笔者能力有限,倘若在阅读时,有哪些不是很懂,欢迎在<a href="https://github.com/yxlazy/blog/issues">这里</a>评论留言,大家一起讨论,共同进步</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Sec-WebSocket-Accept">https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Sec-WebSocket-Accept</a></p><p><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket">https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket</a></p><p><a href="https://juejin.cn/post/6844903463202062343">https://juejin.cn/post/6844903463202062343</a></p><p><a href="https://www.php.cn/http/http-http101.html">https://www.php.cn/http/http-http101.html</a></p><p><a href="https://www.jianshu.com/p/3444ea70b6cb">https://www.jianshu.com/p/3444ea70b6cb</a></p><p><a href="https://zhuanlan.zhihu.com/p/407711596">https://zhuanlan.zhihu.com/p/407711596</a></p><p><a href="https://github.com/sitegui/nodejs-websocket">https://github.com/sitegui/nodejs-websocket</a></p>]]></content>
<summary type="html">本文将通过对nodejs-websocket的源码进行解析,从而深入的搞懂websocket是如何工作的</summary>
<category term="node" scheme="https://www.yxlazy.xyz/categories/node/"/>
<category term="源码解析" scheme="https://www.yxlazy.xyz/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
</entry>
<entry>
<title>在函数组件中,如何实现shouldComponentUpdate</title>
<link href="https://www.yxlazy.xyz/2022/08/04/%E5%9C%A8%E5%87%BD%E6%95%B0%E7%BB%84%E4%BB%B6%E4%B8%AD%EF%BC%8C%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0shouldComponentUpdate/"/>
<id>https://www.yxlazy.xyz/2022/08/04/%E5%9C%A8%E5%87%BD%E6%95%B0%E7%BB%84%E4%BB%B6%E4%B8%AD%EF%BC%8C%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0shouldComponentUpdate/</id>
<published>2022-08-04T09:30:49.000Z</published>
<updated>2023-01-12T08:20:42.170Z</updated>
<content type="html"><![CDATA[<p>在函数组件中,我们该如何实现<code>class</code>组件才有的<code>shouldComponentUpdate()</code>生命周期呢?</p><p>首先我们必须知道<code>shouldComponentUpdate()</code>的作用是什么,官方文档解释的是,<strong>当<code>props</code>和<code>state</code>发生改变时,<code>shouldComponentUpdate()</code>会在渲染之前被调用,默认的返回值是<code>true</code>,当返回值为<code>false</code>时,将会阻止本次渲染</strong></p><p>既然已经知道了它的作用,那我们在函数组件中该如何实现呢?</p><p>在函数组件中,要阻止组件重新渲染就需要使用到<code>React.memo()</code>,<code>React.memo</code>是类似于高阶组件,但只适用于函数组件,我们在第二个参数中,通过返回<code>true</code>来阻止函数组件的渲染。</p><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><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Component</span>, useState, memo } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;<br><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">Child</span> = (<span class="hljs-params">props</span>) => {<br> <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span>{props.type}组件渲染结果:{props.text}<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span>;<br>};<br><br><span class="hljs-comment">// 函数组件</span><br><span class="hljs-keyword">const</span> <span class="hljs-title class_">ContentFn</span> = <span class="hljs-title function_">memo</span>(<span class="hljs-function">(<span class="hljs-params">props</span>) =></span> {<br> <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Child</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'函数'</span> {<span class="hljs-attr">...props</span>} /></span></span>;<br>}, <span class="hljs-function">(<span class="hljs-params">prevProps, nextProps</span>) =></span> {<br> <span class="hljs-comment">// 返回true,组织渲染</span><br> <span class="hljs-keyword">return</span> nextProps.<span class="hljs-property">disabled</span><br>});<br><br><span class="hljs-comment">// 类组件</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ContentClass</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_ inherited__">Component</span> {<br> <span class="hljs-title function_">constructor</span>(<span class="hljs-params">props</span>) {<br> <span class="hljs-variable language_">super</span>(props);<br> }<br><br> <span class="hljs-title function_">shouldComponentUpdate</span>(<span class="hljs-params">nextProps, nextState</span>) {<br> <span class="hljs-comment">// 阻止渲染</span><br> <span class="hljs-keyword">return</span> !nextProps.<span class="hljs-property">disabled</span>;<br> }<br><br> <span class="hljs-title function_">render</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Child</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'类'</span> {<span class="hljs-attr">...this.props</span>} /></span></span>;<br> }<br>}<br><br><span class="hljs-keyword">const</span> <span class="hljs-title function_">App</span> = (<span class="hljs-params"></span>) => {<br> <span class="hljs-keyword">const</span> [text, setText] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">""</span>);<br> <span class="hljs-keyword">const</span> [disabled, setDisabled] = <span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>);<br> <span class="hljs-keyword">return</span> (<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =></span> setText(e.target.value)} /></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =></span> setDisabled((disabled) => !disabled)}></span><br><span class="language-xml"> {disabled ? "正常渲染" : "阻止渲染"}</span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">button</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">ContentFn</span> <span class="hljs-attr">text</span>=<span class="hljs-string">{text}</span> <span class="hljs-attr">disabled</span>=<span class="hljs-string">{disabled}</span> /></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">ContentClass</span> <span class="hljs-attr">text</span>=<span class="hljs-string">{text}</span> <span class="hljs-attr">disabled</span>=<span class="hljs-string">{disabled}</span> /></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br> );<br>};<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">App</span>;<br><br></code></pre></td></tr></table></figure><p>特别说明,<code>shouldComponentUpdate()</code>和<code>memo()</code>两个的返回值相反,<code>shouldComponentUpdate()</code>返回<code>false</code>会跳过渲染,而<code>React.memo()</code>是在返回<code>true</code>的情况下才跳过渲染。</p><p>事实上,<code>React.memo()</code> 并不是阻断渲染,而是跳过渲染组件的操作并直接<strong>复用最近一次渲染</strong>的结果,这与 <code>shouldComponentUpdate()</code> 是完全不同的。</p><p>而对于这两个函数,则都是用于性能优化的手段,在类组件中,官方提供了<code>PrueComponent</code>类组件实现了<code>shouldComponentUpdate()</code>,在函数组件中,<code>React.memo()</code>通常需要搭配<code>useCallback()</code>和<code>useMemo()</code>才能更好的发挥其作用。</p>]]></content>
<summary type="html">在函数组件中,我们该如何实现`class`组件才有的`shouldComponentUpdate()`生命周期呢?</summary>
<category term="react" scheme="https://www.yxlazy.xyz/categories/react/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
<category term="react" scheme="https://www.yxlazy.xyz/tags/react/"/>
</entry>
<entry>
<title>如何实现分享海报</title>
<link href="https://www.yxlazy.xyz/2022/06/30/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%88%86%E4%BA%AB%E6%B5%B7%E6%8A%A5/"/>
<id>https://www.yxlazy.xyz/2022/06/30/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%88%86%E4%BA%AB%E6%B5%B7%E6%8A%A5/</id>
<published>2022-06-30T07:56:15.000Z</published>
<updated>2023-01-12T08:20:42.170Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>今天我们来聊聊关于前端生成海报的问题,一般我们制作海报,都会选择前端进行生成。将需要分享出去的部分的dom直接生成图片,然后把这个图片分享给用户。在这里我们通常使用的是<code>html2canvas</code>这个库,下面我们就来实现一下</p><blockquote><p>html2canvas文档在这里 <a href="http://html2canvas.hertzen.com/">http://html2canvas.hertzen.com</a></p><p>查看本文的完整代码可直接移步到文章末尾</p></blockquote><h2 id="动手时间(挖坑时间)"><a href="#动手时间(挖坑时间)" class="headerlink" title="动手时间(挖坑时间)"></a>动手时间(挖坑时间)</h2><p>根据官方文档安装依赖</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn add html2canvas<br></code></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// from https://mobile.ant.design/zh/components/image</span><br><span class="hljs-keyword">const</span> demoSrc = <br> <span class="hljs-string">'https://images.unsplash.com/photo-1567945716310-4745a6b7844b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=60'</span><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">SharePoster</span>(<span class="hljs-params"></span>) {<br><br> <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{wrapperRef}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'share-poster'</span>></span></span><br><span class="language-xml"> {/**商品的图片 */}</span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{demoSrc}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'share-poster--img'</span> <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{imgeLoad}/</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是另一个标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是还是一个标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br>}<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">SharePoster</span><br></code></pre></td></tr></table></figure><p>现在,内容有了,该是生火…不对,该是生成海报的时候了,添加<code>html2canvas</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">import</span> html2canvas <span class="hljs-keyword">from</span> <span class="hljs-string">'html2canvas'</span><br><span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span><br><span class="hljs-keyword">import</span> <span class="hljs-string">'./index.less'</span><br><br><span class="hljs-keyword">const</span> demoSrc = <br> <span class="hljs-string">'https://images.unsplash.com/photo-1567945716310-4745a6b7844b?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1500&q=60'</span><br><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">SharePoster</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">const</span> wrapperRef = <span class="hljs-title function_">useRef</span>(<span class="hljs-literal">null</span>)<br><br> <span class="hljs-comment">// 等待图片加载完成</span><br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">imgeLoad</span> = (<span class="hljs-params"></span>) => {<br> <span class="hljs-keyword">if</span> (wrapperRef.<span class="hljs-property">current</span>) {<br> <span class="hljs-title function_">html2canvas</span>(wrapperRef.<span class="hljs-property">current</span>, {<br> <span class="hljs-attr">useCORS</span>: <span class="hljs-literal">true</span><br> }).<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">canvas</span> =></span> {<br> <span class="hljs-comment">// 将生成的canvas转换成base64</span><br> <span class="hljs-keyword">const</span> base64 = canvas.<span class="hljs-title function_">toDataURL</span>(<span class="hljs-string">'image/png'</span>)<br> <span class="hljs-comment">// 到这里我们的海报就生成了</span><br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(base64)<br><br> <span class="hljs-comment">// 下载到本地</span><br> <span class="hljs-comment">// const a = document.createElement('a')</span><br> <span class="hljs-comment">// a.href = base64</span><br> <span class="hljs-comment">// a.download = +new Date() + '.png'</span><br> <span class="hljs-comment">// a.click()</span><br> })<br> }<br> }<br><br> <span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{wrapperRef}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'share-poster'</span>></span></span><br><span class="language-xml"> {/**商品的图片 */}</span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{demoSrc}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'share-poster--img'</span> <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{imgeLoad}/</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是另一个标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是还是一个标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br>}<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">SharePoster</span><br></code></pre></td></tr></table></figure><p>恭喜🎉🎉🎉,你在1分钟不到的时间,就完成了产品的需求,我们现在发布到线上吧</p><p>测试:我说大兄弟呀,你这写的啥呀,下载下来的海报,除了文字,商品图片都没有,你是在玩我吗?</p><p>!!!咋回事,本地环境不是没毛病吗</p><h2 id="填坑时间"><a href="#填坑时间" class="headerlink" title="填坑时间"></a>填坑时间</h2><p>首先我们有了解到<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image">canvas不支持加载跨域的图片资源</a>,canvas会认为这回污染画布,而html2canvas用到了canvas,所以就存在转换图片时出错。</p><p>那我们怎么解决这个问题呢,我们可以从另一个方向思考,既然不让直接加载图片,那我们加载base64的图片总可以吧</p><p>将图片转换成base64</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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// from https://www.w3docs.com/snippets/javascript/how-to-convert-the-image-into-a-base64-string-using-javascript.html</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">toDataUrl</span>(<span class="hljs-params">src, callback</span>) {<br> <span class="hljs-keyword">const</span> image = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Image</span>()<br> <span class="hljs-comment">// 执行一个跨域请求</span><br> <span class="hljs-comment">// https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/img#attr-crossorigin</span><br> image.<span class="hljs-property">crossOrigin</span> = <span class="hljs-string">'Anonymous'</span><br> image.<span class="hljs-property">onload</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">const</span> canvas = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">'canvas'</span>)<br> <span class="hljs-keyword">const</span> ctx = canvas.<span class="hljs-title function_">getContext</span>(<span class="hljs-string">'2d'</span>)<br> <span class="hljs-comment">// 将图片画到canvas中</span><br> ctx.<span class="hljs-title function_">drawImage</span>(image, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);<br> <span class="hljs-keyword">const</span> dataURL = canvas.<span class="hljs-title function_">toDataURL</span>(<span class="hljs-string">'image/png'</span>)<br><br> <span class="hljs-title function_">callback</span>(dataURL)<br> }<br> <span class="hljs-comment">// 加载图片</span><br> image.<span class="hljs-property">src</span> = src<br>}<br></code></pre></td></tr></table></figure><p>更改<code>SharePoster</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><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><code class="hljs javascript"><span class="hljs-comment">// ....</span><br><span class="hljs-keyword">const</span> [base64, setBase64] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">''</span>)<br><br><span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-title function_">toDataUrl</span>(demoSrc, setBase64)<br>}, [])<br><br><span class="hljs-comment">// 等待图片转换成base64</span><br><span class="hljs-keyword">if</span> (!base64) {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span><br>}<br><br><span class="hljs-keyword">return</span> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{wrapperRef}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'share-poster'</span>></span></span><br><span class="language-xml"> {/**商品的图片 */}</span><br><span class="language-xml"> {/**现在这里使用base64,而不是图片地址 */}</span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{base64}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'share-poster--img'</span> <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{imgeLoad}/</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是另一个标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span>这是还是一个标题<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="language-xml"><span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br></code></pre></td></tr></table></figure><p>对于做了cdn处理的图片,需要在图片资源尾部加个时间戳,避免加载缓存导致生成海报失败</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><code class="hljs javascript"><span class="hljs-comment">// ...</span><br><br> <span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-title function_">toDataUrl</span>(demoSrc + <span class="hljs-string">'v='</span> + +<span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(), setBase64)<br> }, [])<br><span class="hljs-comment">// ...</span><br></code></pre></td></tr></table></figure><blockquote><p>完整代码 <a href="https://codesandbox.io/s/share-poster-vg3gt6">https://codesandbox.io/s/share-poster-vg3gt6</a></p></blockquote>]]></content>
<summary type="html">在实现h5页面时,如果产品需要我们制作一个分享海报用于分享给用户从而引流,那我们该怎么去实现这个分享海报呢?🤔</summary>
<category term="react" scheme="https://www.yxlazy.xyz/categories/react/"/>
<category term="react" scheme="https://www.yxlazy.xyz/tags/react/"/>
<category term="h5" scheme="https://www.yxlazy.xyz/tags/h5/"/>
</entry>
<entry>
<title>使用husky+lint-staged规范代码格式</title>
<link href="https://www.yxlazy.xyz/2022/03/03/%E4%BD%BF%E7%94%A8husky+lint-staged%E8%A7%84%E8%8C%83%E4%BB%A3%E7%A0%81%E6%A0%BC%E5%BC%8F/"/>
<id>https://www.yxlazy.xyz/2022/03/03/%E4%BD%BF%E7%94%A8husky+lint-staged%E8%A7%84%E8%8C%83%E4%BB%A3%E7%A0%81%E6%A0%BC%E5%BC%8F/</id>
<published>2022-03-03T02:21:58.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<ol><li>安装<code>eslint, prettier</code>和<code>lint-staged</code><blockquote><p>此处并不打算列出eslintrc.js和prettierrc.js的具体配置,具体配置请自行百度</p></blockquote></li></ol><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><code class="hljs bash">yarn add eslint prettier lint-staged --dev<br>npx husky-init && yarn<br></code></pre></td></tr></table></figure><ol start="2"><li>更改<code>pre-commit.sh</code>下的命令为<br><code>npx lint-staged</code></li></ol><blockquote><p><code>pre-commit</code> 是执行<code>git commit</code>时的<code>hook</code></p></blockquote><ol start="3"><li>在package.json下配置</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js">{<br> <span class="hljs-string">"lint-staged"</span>: {<br> <span class="hljs-string">"**/*.{js, jsx, ts, tsx}"</span>: [<br> <!-- 每次commit时,都将更改的文件进行格式检查并格式化 --><br> <span class="hljs-string">"eslint --fix"</span>,<br> <!-- 如果有格式化的文件,就将其添加到暂存区,方便commit提交文件 --><br> <span class="hljs-string">"git add"</span><br> ]<br> }<br>}<br></code></pre></td></tr></table></figure><ol start="4"><li>在eslint使用prettier</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">yarn add eslint-plugin-prettier eslint-config-prettier --dev<br></code></pre></td></tr></table></figure><ol start="5"><li>在eslintrc.js文件添加如下配置</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs js">{<br> <!-- 一定要放到最后面,才能覆盖其他配置 注:v8之后就是使用如下格式--><br> <span class="hljs-attr">extends</span>: [<span class="hljs-string">'prettier'</span>],<br> <span class="hljs-attr">plugins</span>: [<span class="hljs-string">'prettier'</span>],<br> <span class="hljs-attr">rules</span>: {<br> <!-- 使用prettier, 默认会使用.<span class="hljs-property">prettierrc</span>.<span class="hljs-property">js</span>中的规则 --><br> <span class="hljs-string">"prettier/prettier"</span>: <span class="hljs-string">"error"</span><br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<summary type="html">拒绝💩一样的代码提交到git仓库...</summary>
<category term="other" scheme="https://www.yxlazy.xyz/categories/other/"/>
<category term="代码规范" scheme="https://www.yxlazy.xyz/tags/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
</entry>
<entry>
<title>axios源码学习</title>
<link href="https://www.yxlazy.xyz/2022/03/01/axios%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/"/>
<id>https://www.yxlazy.xyz/2022/03/01/axios%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/</id>
<published>2022-03-01T15:33:53.000Z</published>
<updated>2023-01-12T08:20:42.158Z</updated>
<content type="html"><![CDATA[<p><code>axios</code>是一个轻量无依赖的<code>Promise</code>版的<code>http</code>请求库,不仅可以在浏览器端使用,也可以在node端使用,今天我们就来学习一下这款备受欢迎的库。</p><p>在使用<code>axios</code>时,我们可以通过以下方式发起一个<code>axios</code>请求</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-title function_">axios</span>({})<br><span class="hljs-keyword">new</span> axios.<span class="hljs-title class_">Axios</span>({})<br>axios.<span class="hljs-title function_">create</span>({})<br>axios[<span class="hljs-string">'get'</span>]()<br></code></pre></td></tr></table></figure><p>其实最后都是调用的<code>Axios.prototype.request</code>方法,我们可以看一下<code>createInstance()</code>这个函数到底做了什么,可以看到,这个函数主要就是用户创建一个<code>axios</code>对象:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">createInstance</span>(<span class="hljs-params">defaultConfig</span>) {<br> <span class="hljs-comment">// 创建一个axios对象</span><br> <span class="hljs-keyword">var</span> context = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Axios</span>(defaultConfig);<br> <span class="hljs-comment">// 绑定Axios.prototype.request中的this并创建一个instance变量</span><br> <span class="hljs-keyword">var</span> instance = <span class="hljs-title function_">bind</span>(<span class="hljs-title class_">Axios</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">request</span>, context);<br> <span class="hljs-comment">// 将Axios原型对象扩展到instance上,并绑定this</span><br> <span class="hljs-comment">// Copy axios.prototype to instance</span><br> utils.<span class="hljs-title function_">extend</span>(instance, <span class="hljs-title class_">Axios</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>, context);<br> <span class="hljs-comment">// 将axios对象上的属性扩展到instance上</span><br> <span class="hljs-comment">// Copy context to instance</span><br> utils.<span class="hljs-title function_">extend</span>(instance, context);<br><br> <span class="hljs-comment">// 暴露出一个create方法,可以通过这个方法创建axios对象</span><br> <span class="hljs-comment">// Factory for creating new instances</span><br> instance.<span class="hljs-property">create</span> = <span class="hljs-keyword">function</span> <span class="hljs-title function_">create</span>(<span class="hljs-params">instanceConfig</span>) {<br> <span class="hljs-keyword">return</span> <span class="hljs-title function_">createInstance</span>(<span class="hljs-title function_">mergeConfig</span>(defaultConfig, instanceConfig));<br> };<br> <span class="hljs-comment">// 返回instance对象,这个就是新的axios对象了</span><br> <span class="hljs-keyword">return</span> instance;<br>}<br><span class="hljs-comment">// 新的axios对象</span><br><span class="hljs-keyword">var</span> axios = <span class="hljs-title function_">createInstance</span>(defaults);<br><span class="hljs-comment">// Axios构造函数绑定到axios上</span><br>axios.<span class="hljs-property">Axios</span> = <span class="hljs-title class_">Axios</span>;<br></code></pre></td></tr></table></figure><p>下面我们着重看一下<code>Axios.prototype.request</code>方法, 方法前面部分都是对配置的一些处理(就不细讲了),<br>然后从这里开始(也就是下面代码所示),这是<code>axios</code>的拦截器处理。</p><p>首先看一下请求拦截器:<br>请求拦截器会依次将<code>interceptor.fulfilled</code>和<code>interceptor.rejected</code>往数组(<code>requestInterceptorChain</code>)的前面放</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// lib\core\Axios.js</span><br><span class="hljs-comment">// 用于存储请求拦截器的回调</span><br> <span class="hljs-keyword">var</span> requestInterceptorChain = [];<br> <span class="hljs-comment">// 在对请求拦截器进行处理时,是采用同步处理方式还是采用异步处理的方式,默认在没有添加拦截器时时同步,添加拦截器后是异步</span><br> <span class="hljs-keyword">var</span> synchronousRequestInterceptors = <span class="hljs-literal">true</span>;<br> <span class="hljs-comment">// 遍历添加的拦截器</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">interceptors</span>.<span class="hljs-property">request</span>.<span class="hljs-title function_">forEach</span>(<span class="hljs-keyword">function</span> <span class="hljs-title function_">unshiftRequestInterceptors</span>(<span class="hljs-params">interceptor</span>) {<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> interceptor.<span class="hljs-property">runWhen</span> === <span class="hljs-string">'function'</span> && interceptor.<span class="hljs-title function_">runWhen</span>(config) === <span class="hljs-literal">false</span>) {<br> <span class="hljs-keyword">return</span>;<br> }<br> <span class="hljs-comment">// interceptor.synchronous来自添加拦截器时,所配置的选项</span><br> synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.<span class="hljs-property">synchronous</span>;<br> <span class="hljs-comment">// 这里将每个迭代器以fulfilled和rejected为一组的形式,依次放到数组的前面</span><br> requestInterceptorChain.<span class="hljs-title function_">unshift</span>(interceptor.<span class="hljs-property">fulfilled</span>, interceptor.<span class="hljs-property">rejected</span>);<br> });<br></code></pre></td></tr></table></figure><p>响应拦截器处理:<br>响应拦截器会依次将<code>interceptor.fulfilled</code>和<code>interceptor.rejected</code>往数组(<code>responseInterceptorChain</code>)的后面放</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 用于存储响应拦截器的回调</span><br> <span class="hljs-keyword">var</span> responseInterceptorChain = [];<br> <span class="hljs-comment">// 遍历添加的拦截器</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">interceptors</span>.<span class="hljs-property">response</span>.<span class="hljs-title function_">forEach</span>(<span class="hljs-keyword">function</span> <span class="hljs-title function_">pushResponseInterceptors</span>(<span class="hljs-params">interceptor</span>) {<br> <span class="hljs-comment">// 这里将每个迭代器以fulfilled和rejected为一组的形式,依次放到数组的后面</span><br> responseInterceptorChain.<span class="hljs-title function_">push</span>(interceptor.<span class="hljs-property">fulfilled</span>, interceptor.<span class="hljs-property">rejected</span>);<br> });<br></code></pre></td></tr></table></figure><p>为什么会如此放置呢?在讨论这个问题之前,我们先来看一下这个拦截器(interceptors)是怎么存放我们注册的拦截器的。拦截器是在我们创建axios实例的时候创建的,它们都是同一个构造函数的两个不同的实例:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// lib\core\Axios.js</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">Axios</span>(<span class="hljs-params">instanceConfig</span>) {<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">defaults</span> = instanceConfig;<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">interceptors</span> = {<br> <span class="hljs-comment">// 创建一个请求拦截器对象</span><br> <span class="hljs-attr">request</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">InterceptorManager</span>(),<br> <span class="hljs-comment">// 创建一个响应拦截器对象</span><br> <span class="hljs-attr">response</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">InterceptorManager</span>()<br> };<br>}<br></code></pre></td></tr></table></figure><p>拦截器的构造函数。当我们在调用axios.interceptors.request.use()时,其实就是向这个this.handlers的末尾注册一个拦截器回调</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//lib\core\InterceptorManager.js</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">InterceptorManager</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-comment">// 存放添加的拦截器回调</span><br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">handlers</span> = [];<br>}<br><br><span class="hljs-comment">// 注册拦截器</span><br><span class="hljs-title class_">InterceptorManager</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">use</span> = <span class="hljs-keyword">function</span> <span class="hljs-title function_">use</span>(<span class="hljs-params">fulfilled, rejected, options</span>) {<br> <span class="hljs-variable language_">this</span>.<span class="hljs-property">handlers</span>.<span class="hljs-title function_">push</span>({<br> <span class="hljs-attr">fulfilled</span>: fulfilled,<br> <span class="hljs-attr">rejected</span>: rejected,<br> <span class="hljs-attr">synchronous</span>: options ? options.<span class="hljs-property">synchronous</span> : <span class="hljs-literal">false</span>,<br> <span class="hljs-attr">runWhen</span>: options ? options.<span class="hljs-property">runWhen</span> : <span class="hljs-literal">null</span><br> });<br> <span class="hljs-comment">// 返回一个已注册拦截器的id,用于注销添加的拦截器</span><br> <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">handlers</span>.<span class="hljs-property">length</span> - <span class="hljs-number">1</span>;<br>};<br><br><span class="hljs-comment">// 这是上面迭代拦截器的方法,拦截器对象自己实现了一个迭代方法</span><br><span class="hljs-title class_">InterceptorManager</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">forEach</span> = <span class="hljs-keyword">function</span> <span class="hljs-title function_">forEach</span>(<span class="hljs-params">fn</span>) {<br> utils.<span class="hljs-title function_">forEach</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">handlers</span>, <span class="hljs-keyword">function</span> <span class="hljs-title function_">forEachHandler</span>(<span class="hljs-params">h</span>) {<br> <span class="hljs-keyword">if</span> (h !== <span class="hljs-literal">null</span>) {<br> <span class="hljs-title function_">fn</span>(h);<br> }<br> });<br>};<br></code></pre></td></tr></table></figure><p>接着我们来看下上面我们提出的问题,为什么会如此排列?<br>看代码上的注释</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs js"><span class="hljs-comment">// 用来保存上一次promise.then处理后的结果</span><br><span class="hljs-keyword">var</span> promise;<br><span class="hljs-comment">// 将请求拦截器处理放到promise中进行异步处理</span><br><span class="hljs-keyword">if</span> (!synchronousRequestInterceptors) {<br> <span class="hljs-comment">// 存储所有的操作,包括请求拦截器和响应拦截器的处理,dispatchRequest用来发起请求, 设置undefined是为了占位</span><br> <span class="hljs-keyword">var</span> chain = [dispatchRequest, <span class="hljs-literal">undefined</span>];<br> <span class="hljs-comment">// 请求拦截器和响应拦截器一前一后放入chain中</span><br> <span class="hljs-title class_">Array</span>.<span class="hljs-property"><span class="hljs-keyword">prototype</span></span>.<span class="hljs-property">unshift</span>.<span class="hljs-title function_">apply</span>(chain, requestInterceptorChain);<br> chain = chain.<span class="hljs-title function_">concat</span>(responseInterceptorChain);<br></code></pre></td></tr></table></figure><p>这里做的处理,我觉得可以说是非常华丽了(一只菜汪路过)</p><ol><li><p>我们前面已经将所有的拦截器都放入了chain数组中,而且存放的格式是这样的<br><code>[fulfilled1, rejected1, fulfilled2, rejected2, ...dispatchRequest, undefined, fulfilled1, rejected1,fulfilled2, rejected2, ...]</code></p></li><li><p>这里通过一个while循环来遍历这个数组(也就是chain),并进行处理</p></li><li><p><code>promise = promise.then(chain.shift(), chain.shift());</code> 就是用来队列处理整个chain</p></li><li><p>第一次循环<code>promise = promise.then(fulfilled1, rejected1)</code>处理第一个拦截器回调,等处理结束后赋值给promise,</p></li><li><p>接着第二次进入循环<code>promise = promise.then(fulfilled2, rejected2)</code><br>…</p></li><li><p>当处理到<code>dispatchRequest, undefined</code>时,证明请求拦截器已经处理完成,开始发起请求,等请求结果回来,进入响应拦截器的处理</p></li><li><p><code>promise = promise.then(fulfilled1, rejected1)</code>,同样的依次进行每个拦截器的回调</p></li><li><p>最后返回这个经过请求拦截器处理,发起请求处理,和响应拦截器处理的结果 – promise</p></li></ol><blockquote><p>注:在处理请求拦截器时,<em>最先添加到请求拦截器的回调,会最后执行;相反,对于响应拦截器的处理是,最先添加到响应拦截器的回调最先执行</em></p></blockquote><p>几句代码,但这分量确实足(学到了)</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs js"> promise = <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">resolve</span>(config);<br> <span class="hljs-keyword">while</span> (chain.<span class="hljs-property">length</span>) {<br> promise = promise.<span class="hljs-title function_">then</span>(chain.<span class="hljs-title function_">shift</span>(), chain.<span class="hljs-title function_">shift</span>());<br> }<br><br> <span class="hljs-keyword">return</span> promise;<br>}<br></code></pre></td></tr></table></figure><blockquote><p>这里是把请求拦截处理当做异步来处理的,对于同步处理,是将整个处理分为了两部分进行处理的,第一部分是请求拦截器部分,是同步处理的,第二部分是发起请求的部分和响应拦截器部分合在一起进行异步处理,就和上面的一样了</p></blockquote><p>总结:可以看到在promise.then处理的时候,正好一个是fulfilled回调,一个是rejected回调,所以,我们将拦截器注册的回调也依次按这样排列就正好方便我们处理。不仅如此,我们将真正发起请求的处理也按着成对的逻辑并放置在中间,就能保证处理的顺序是按照:处理请求拦截器 => 发起请求 => 处理响应拦截器的顺序</p>]]></content>
<summary type="html">axios是一个轻量无依赖的Promise版的http请求库,不仅可以在浏览器端使用,也可以在node端使用,今天我们就来学习一下这款备受欢迎的库。</summary>
<category term="other" scheme="https://www.yxlazy.xyz/categories/other/"/>
<category term="源码解析" scheme="https://www.yxlazy.xyz/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
</entry>
<entry>
<title>react-router v6 api学习</title>
<link href="https://www.yxlazy.xyz/2022/02/15/react-router%20v6%20api%E5%AD%A6%E4%B9%A0/"/>
<id>https://www.yxlazy.xyz/2022/02/15/react-router%20v6%20api%E5%AD%A6%E4%B9%A0/</id>
<published>2022-02-15T15:53:52.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>react router目前已经更新到v6版本,该版本不仅使用<code>TypeScript</code>进行了重构,而且还新增了很多的功能,其与v5版本可以说已完全没有关联。<br>现在v6已成为默认安装版本。</p><p>react router v6分为3个包:</p><ul><li>react-router: 包含核心功能</li><li>react-router-dom: web端使用的包</li><li>react-router-native: rn端使用的包<blockquote><p>react-router-dom和react-router-native已经自动引入了react-router,所以我们在开发时,只需要安装对应开发环境的包即可</p></blockquote></li></ul><h2 id="引用路由组件"><a href="#引用路由组件" class="headerlink" title="引用路由组件"></a>引用路由组件</h2><p>对于web端,支持两种形式的路由:</p><ul><li><code><HashRouter /></code>: hash路由,当路由发生改变不会重新向服务端发起请求</li><li><code><BrowserRouter /></code>: 使用浏览器内置历史堆栈进行导航</li></ul><blockquote><p>新的组件已不再需要显示添加history,其已经在组件内部实现</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 使用hash</span><br><span class="hljs-keyword">import</span> {<span class="hljs-title class_">HashRouter</span>, <span class="hljs-title class_">Routes</span>, <span class="hljs-title class_">Route</span>} <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">return</span>(<br> <HashRouter><br> <Routes><br> <Route path='/' element={<Home />}><br> <Route path='/about' element={<About />}><br> </Routes><br> </HashRouter><br> )<br>}<br><br>// 不使用hash<br>import {BrowserRouter, Routes} from 'react-router-dom'<br><br>function App() {<br> return(<br> <BrowserRouter><br> <Routes><br> <Route path='/' element={<Home />}><br> <Route path='/about' element={<About />}><br> </Routes><br> </BrowserRouter><br> )<br>}<br></code></pre></td></tr></table></figure><blockquote><p>注意,如果不使用hash,则每个路由必须要有一个与之对应的服务端路由,否则会出现404</p></blockquote><p><code><NativeRouter /></code>: React Native端使用的路由</p><p><code><MemoryRouter /></code>: 其将路由存储在一个数组中,不依赖外部源,可以适合各种场景,例如测试环境下</p><h3 id="lt-Routes-gt"><a href="#lt-Routes-gt" class="headerlink" title="<Routes />"></a><code><Routes /></code></h3><p>检查与之匹配的path,并渲染对应的<code><Route /></code></p><h3 id="lt-Route-gt"><a href="#lt-Route-gt" class="headerlink" title="<Route />"></a><code><Route /></code></h3><p>渲染匹配path的React Element</p><h2 id="路由跳转"><a href="#路由跳转" class="headerlink" title="路由跳转"></a>路由跳转</h2><h3 id="lt-Link-gt"><a href="#lt-Link-gt" class="headerlink" title="<Link />"></a><code><Link /></code></h3><p>其内部会渲染一个a标签,点击会跳转到对应的路由</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js"><<span class="hljs-title class_">Link</span> to=<span class="hljs-string">'/home'</span>>跳转到home路径</<span class="hljs-title class_">Link</span>><br></code></pre></td></tr></table></figure><h3 id="lt-NavLink-gt"><a href="#lt-NavLink-gt" class="headerlink" title="<NavLink />"></a><code><NavLink /></code></h3><p>是一个特殊的<code><Link /></code>组件,常用于渲染导航栏选中时的高亮</p><figure class="highlight ts"><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><code class="hljs ts"><<span class="hljs-title class_">NavLink</span> style={<span class="hljs-function">(<span class="hljs-params">{isActive: <span class="hljs-built_in">boolean</span>}</span>) =></span> isActive ? <span class="hljs-string">'blue'</span> : <span class="hljs-string">'white'</span>}><br> <span class="hljs-title class_">Home</span><br></<span class="hljs-title class_">NavLink</span>><br></code></pre></td></tr></table></figure><h3 id="lt-Navigate-gt"><a href="#lt-Navigate-gt" class="headerlink" title="<Navigate />"></a><code><Navigate /></code></h3><p>对<code>useNavigate()</code>的封装</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js"><<span class="hljs-title class_">Navigate</span> to=<span class="hljs-string">'/home'</span> /><br></code></pre></td></tr></table></figure><blockquote><p>这个组件只是为了在Class组件写法中使用,如果是使用Function组件,建议使用hooks写法</p></blockquote><h3 id="useNavigate"><a href="#useNavigate" class="headerlink" title="useNavigate():"></a><code>useNavigate()</code>:</h3><p>改变路由:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> navigate = <span class="hljs-title function_">useNavigate</span>();<br><br><span class="hljs-comment">// 跳转到/login路由</span><br><span class="hljs-title function_">navigate</span>(<span class="hljs-string">'/login'</span>);<br><br><span class="hljs-comment">// 默认跳转类型为PUSH, 可以设置跳转的类型为REPLACE</span><br><span class="hljs-title function_">navigate</span>(<span class="hljs-string">'/login'</span>, {<span class="hljs-attr">replace</span>: <span class="hljs-literal">true</span>});<br><br><span class="hljs-comment">// 可以直接传递数值跳转到指定某个记录,与window.history.go()类似</span><br><span class="hljs-title function_">navigate</span>(-<span class="hljs-number">1</span>);<br></code></pre></td></tr></table></figure><h2 id="使用嵌套路由"><a href="#使用嵌套路由" class="headerlink" title="使用嵌套路由"></a>使用嵌套路由</h2><h3 id="lt-Outlet-gt"><a href="#lt-Outlet-gt" class="headerlink" title="<Outlet />"></a><code><Outlet /></code></h3><p>实现嵌套路由的渲染</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs js"><span class="hljs-title class_">ReactDom</span>.<span class="hljs-title function_">render</span>(<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Routes</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/'</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span><<span class="hljs-attr">App</span> /></span>}> </span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/home'</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span><<span class="hljs-attr">Home</span> /></span>} /></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">Route</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">Routes</span>></span></span>,<br> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'root'</span>);<br>);<br><span class="hljs-comment">// App.js</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">return</span>(<br> <div><br> <p>渲染App组件<p><br> {/**如果不添加这个,当path匹配/home时,页面将不会渲染Home组件**/}<br> <Outlet /><br> </div><br> );<br>}<br></code></pre></td></tr></table></figure><h3 id="useOutlet"><a href="#useOutlet" class="headerlink" title="useOutlet()"></a><code>useOutlet()</code></h3><p>返回路由层次结构中此级别的子路由的元素, <code><Outlet /></code>的hooks版</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// App.js</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">const</span> outlet = <span class="hljs-title function_">useOutlet</span>();<br> <span class="hljs-keyword">return</span>(<br> <div><br> <p>渲染App组件<p><br> {/**如果不添加这个,当path匹配/home时,页面将不会渲染Home组件**/}<br> {outlet}<br> </div><br> );<br>}<br></code></pre></td></tr></table></figure><h3 id="useOutLetContext"><a href="#useOutLetContext" class="headerlink" title="useOutLetContext()"></a><code>useOutLetContext()</code></h3><p>用来向子组件传递属性</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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><code class="hljs js"><span class="hljs-comment">// App.js</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">const</span> [message, setMessage] = <span class="hljs-title function_">useState</span>(<span class="hljs-string">""</span>);<br> <span class="hljs-keyword">return</span> (<br> <div><br> <p>渲染App组件<p><br> {/**通过context向子组件广播属性**/}<br> <Outlet context={[message, setMessage]} /><br> </div><br> );<br>}<br><br>// Home.js<br>function Home() {<br> // 子组件通过useOutletContext() 拿到父组件传递的属性<br> const [message, setMessage] = useOutletContext();<br> return (<br> <div><br> home<br> <p>{message}</p><br> <input onChange={(e) => setMessage(e.target.value)} /><br> </div><br> );<br>}<br></code></pre></td></tr></table></figure><h2 id="Index-Routes-概念"><a href="#Index-Routes-概念" class="headerlink" title="Index Routes 概念"></a>Index Routes 概念</h2><p>什么叫”index route”?</p><ul><li>index route 渲染在父路由的outlet</li><li>当一个父路由被匹配时,其他子路由都未被匹配,将会匹配index route,</li><li>index route 是父路由的默认子路由</li><li>如果用户尚未点击导航的任何一个路由时,index route将会被渲染</li></ul><blockquote><p>可能我理解的不是很准确,可以查看<a href="https://reactrouter.com/docs/en/v6/getting-started/tutorial#index-routes">官方的解释</a></p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-title class_">ReactDom</span>.<span class="hljs-title function_">render</span>(<br> <Routes><br> <Route path='/' element={<App />}> <br> <Route path='/home' element={<Home />} /><br> <Route path='/about' element={<About />}><br> </Route><br> </Routes>,<br> document.getElementById('root');<br>);<br>// App.js<br>function App() {<br> return(<br> <div><br> <p>渲染App组件<p><br> {/**如果不添加这个,当path匹配/home时,页面将不会渲染Home组件**/}<br> <Outlet /><br> </div><br> );<br>}<br></code></pre></td></tr></table></figure><p>对于上面的例子,当path为’/‘时,此时的渲染组件为:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js"><<span class="hljs-title class_">App</span> /><br></code></pre></td></tr></table></figure><p>如果添加一个Index Route路由:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-title class_">ReactDom</span>.<span class="hljs-title function_">render</span>(<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Routes</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/'</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span><<span class="hljs-attr">App</span> /></span>}> </span><br><span class="language-xml"> {/** Index Route **/}</span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">index</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span><<span class="hljs-attr">Home</span> /></span>} /></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/about'</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span><<span class="hljs-attr">About</span> /></span>}></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">Route</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">Routes</span>></span>,</span><br><span class="language-xml"> document.getElementById('root');</span><br><span class="language-xml">);</span><br></code></pre></td></tr></table></figure><p>当path=’/‘时,此时渲染的组件为:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><<span class="hljs-title class_">App</span>><br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Home</span> /></span></span><br></<span class="hljs-title class_">App</span>><br></code></pre></td></tr></table></figure><h2 id="Not-Found-Routes-概念"><a href="#Not-Found-Routes-概念" class="headerlink" title="Not Found Routes 概念"></a>Not Found Routes 概念</h2><p>当没有路由能够匹配,我们常常会添加一个NotFound页面:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><<span class="hljs-title class_">Routes</span>><br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/'</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span><<span class="hljs-attr">Home</span> /></span>}></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'*'</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span><<span class="hljs-attr">NotFound</span> /></span>}></span><br><span class="language-xml"><span class="hljs-tag"></<span class="hljs-name">Routes</span>></span></span><br></code></pre></td></tr></table></figure><p><code>path='*'</code>将匹配所有的URL,但优先级最低,只有不存在匹配的路由时才会匹配它(尝试了下,这个匹配完全不依赖放置的顺序,即你可以任意放置<code>path='*'</code>路由的位置🤔感觉比以前好用了,心智模式至少得到了降低)</p><h2 id="hooks"><a href="#hooks" class="headerlink" title="hooks"></a>hooks</h2><p>这里介绍前面没有介绍的hook</p><h3 id="useHref"><a href="#useHref" class="headerlink" title="useHref()"></a>useHref()</h3><p>传入一个<code>To</code>类型的数据,返回一个拼好的URL, 可以用来放在属性<code>to</code>的位置</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs ts"><span class="hljs-comment">// type To = Partial<Location> | string;</span><br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">to</span>: <span class="hljs-title class_">To</span> = {<br> <span class="hljs-attr">pathname</span>: <span class="hljs-string">"/123456"</span>,<br> <span class="hljs-attr">search</span>: <span class="hljs-string">"a=b&c=d"</span>,<br> <span class="hljs-attr">hash</span>: <span class="hljs-string">"home"</span><br>};<br><span class="hljs-comment">// const href = useHref('/home?a=b&c=d');</span><br><span class="hljs-keyword">const</span> href = <span class="hljs-title function_">useHref</span>(to);<br><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{href}</span> /></span></span><br></code></pre></td></tr></table></figure><h3 id="useLinkClickHandler"><a href="#useLinkClickHandler" class="headerlink" title="useLinkClickHandler()"></a>useLinkClickHandler()</h3><p>返回一个点击事件回调,通常用于自定义<code><Link /></code>组件</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> handleClick = <span class="hljs-title function_">useLinkClickHandler</span>(to);<br><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>></span>点我跳转<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br></code></pre></td></tr></table></figure><h3 id="useLinkPressHandler"><a href="#useLinkPressHandler" class="headerlink" title="useLinkPressHandler()"></a>useLinkPressHandler()</h3><p>同useLinkClickHandler(), 这是用于rn端的</p><h3 id="useInRouterContext"><a href="#useInRouterContext" class="headerlink" title="useInRouterContext()"></a>useInRouterContext()</h3><p>用于判断组件是否在<code><Router /></code>中渲染, true为是,false为否</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> isInRouterRender = <span class="hljs-title function_">useInRouterContext</span>();<br><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> {isInRouterRender ? '组件渲染在<span class="hljs-tag"><<span class="hljs-name">Router</span> /></span>中' : '组件可能飞了'}</span><br><span class="language-xml"><span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br></code></pre></td></tr></table></figure><h3 id="useNavigationType"><a href="#useNavigationType" class="headerlink" title="useNavigationType()"></a>useNavigationType()</h3><p>返回进入当前页面的类型或当前导航类型</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-keyword">type</span> <span class="hljs-title class_">NavigationType</span> = <span class="hljs-string">"POP"</span> | <span class="hljs-string">"PUSH"</span> | <span class="hljs-string">"REPLACE"</span>;<br></code></pre></td></tr></table></figure><blockquote><p>页面初次加载是NavigationType为’POP’类型, 后面如果不设置,默认是’PUSH’类型</p></blockquote><h3 id="useMatch"><a href="#useMatch" class="headerlink" title="useMatch()"></a>useMatch()</h3><p>返回给定路径上相对于当前位置的路由的匹配数据。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs ts"><span class="hljs-keyword">interface</span> <span class="hljs-title class_">PathMatch</span><<span class="hljs-title class_">ParamKey</span> <span class="hljs-keyword">extends</span> <span class="hljs-built_in">string</span> = <span class="hljs-built_in">string</span>> {<br> <span class="hljs-attr">params</span>: <span class="hljs-title class_">Params</span><<span class="hljs-title class_">ParamKey</span>>;<br> <span class="hljs-attr">pathname</span>: <span class="hljs-built_in">string</span>;<br> <span class="hljs-attr">pattern</span>: <span class="hljs-title class_">PathPattern</span>;<br>}<br><br><span class="hljs-keyword">interface</span> <span class="hljs-title class_">PathPattern</span> {<br> <span class="hljs-attr">path</span>: <span class="hljs-built_in">string</span>;<br> caseSensitive?: <span class="hljs-built_in">boolean</span>;<br> end?: <span class="hljs-built_in">boolean</span>;<br>}<br><br><span class="hljs-keyword">const</span> <span class="hljs-attr">match</span>: <span class="hljs-title class_">PathMatch</span><<span class="hljs-title class_">ParamKey</span>> | <span class="hljs-literal">null</span> = <span class="hljs-title function_">useMatch</span>(<span class="hljs-string">'/home'</span>);<br></code></pre></td></tr></table></figure><h3 id="useParams"><a href="#useParams" class="headerlink" title="useParams()"></a>useParams()</h3><p>返回当前URL中的params,并组成一个键/值对对象</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> <span class="hljs-title class_">React</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;<br><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Routes</span>, <span class="hljs-title class_">Route</span>, useParams } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>;<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">ProfilePage</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-comment">// Get the userId param from the URL.</span><br> <span class="hljs-keyword">let</span> { userId } = <span class="hljs-title function_">useParams</span>();<br> <span class="hljs-comment">// ...</span><br>}<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">return</span> (<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Routes</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"users"</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">":userId"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span><<span class="hljs-attr">ProfilePage</span> /></span>} /></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"me"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{...}</span> /></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">Route</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">Routes</span>></span></span><br> );<br>}<br></code></pre></td></tr></table></figure><h3 id="useResolvedPath"><a href="#useResolvedPath" class="headerlink" title="useResolvedPath()"></a>useResolvedPath()</h3><p>返回一个解析后的Path对象</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs ts"><span class="hljs-keyword">declare</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">useResolvedPath</span>(<span class="hljs-params">to: To</span>): <span class="hljs-title class_">Path</span>;<br><br><span class="hljs-keyword">interface</span> <span class="hljs-title class_">Path</span> {<br> <span class="hljs-attr">pathname</span>: <span class="hljs-built_in">string</span>;<br> <span class="hljs-attr">search</span>: <span class="hljs-built_in">string</span>;<br> <span class="hljs-attr">hash</span>: <span class="hljs-built_in">string</span>;<br>}<br></code></pre></td></tr></table></figure><h3 id="useRoutes"><a href="#useRoutes" class="headerlink" title="useRoutes()"></a>useRoutes()</h3><p>等同于<code><Routes /></code>,接收一个和<code><Route /></code>一样参数的对象组成的数组,还是看例子吧:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs js"> <span class="hljs-comment">// 我们将上面的ReactDOM.render渲染用hooks重写</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">Root</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">const</span> element = <span class="hljs-title function_">useRoutes</span>([<br> {<br> <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>,<br> <span class="hljs-attr">element</span>: <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">App</span> /></span></span>,<br> <span class="hljs-attr">children</span>: [<br> {<br> <span class="hljs-attr">path</span>: <span class="hljs-string">'/home'</span>,<br> <span class="hljs-attr">element</span>: <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Home</span> /></span></span><br> },<br> {<br> <span class="hljs-attr">path</span>: <span class="hljs-string">'/about'</span>,<br> <span class="hljs-attr">element</span>: <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">About</span> /></span></span><br> }<br> ]<br> },<br> <span class="hljs-comment">// 添加一个新的路由</span><br> { <span class="hljs-attr">path</span>: <span class="hljs-string">"team"</span>, <span class="hljs-attr">element</span>: <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">AboutPage</span> /></span></span> }<br> ]);<br><br> <span class="hljs-keyword">return</span> element;<br>}<br><br><span class="hljs-title class_">ReactDOM</span>.<span class="hljs-title function_">render</span>(<span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Root</span> /></span></span>, <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'root'</span>));<br></code></pre></td></tr></table></figure><h3 id="useSearchParams"><a href="#useSearchParams" class="headerlink" title="useSearchParams()"></a>useSearchParams()</h3><p>用来操作URL的search参数,从此我们不在需要自己去写一个解析search的工具函数了。使用方式与React.useState一样:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// http://localhost?id=1</span><br><span class="hljs-keyword">const</span> [searchParams, setSearchParams] = <span class="hljs-title function_">useSearchParams</span>();<br><br><span class="hljs-comment">// 获得id字段得值</span><br>searchParams.<span class="hljs-title function_">get</span>(<span class="hljs-string">'id'</span>);<br><br><span class="hljs-comment">// 获取所有的id字段值,返回数组</span><br>searchParams.<span class="hljs-title function_">getAll</span>(<span class="hljs-string">'id'</span>); <span class="hljs-comment">// => ['1']</span><br><br><span class="hljs-comment">// 更改id字段得值</span><br><span class="hljs-title function_">setSearchParams</span>({<span class="hljs-attr">id</span>: <span class="hljs-number">2</span>});<br></code></pre></td></tr></table></figure><p>searchParams是一个URLSearchParams类型实例, setSearchParams其效果与navigate(useNavigate()的返回值)类似,只是前者只改变url的search段</p><blockquote><p>关于URLSearchParams类型实例,详情查看 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams">https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams</a><br>注意URLSearchParams的兼容性</p></blockquote><h3 id="useLocation"><a href="#useLocation" class="headerlink" title="useLocation()"></a>useLocation()</h3><p>返回当前所在页面的location对象(和window.location还是有区别的)。可以用来处理一些当前页面的location发生改变时的副作用</p><blockquote><p>有些api并没有在这里提及</p><ul><li><a href="https://reactrouter.com/docs/en/v6/api#router"><code><Router></code></a></li><li><a href="https://reactrouter.com/docs/en/v6/api#staticrouter"><code><StaticRouter></code></a></li><li><a href="https://reactrouter.com/docs/en/v6/api#createroutesfromchildren">createRoutesFromChildren</a></li><li><a href="https://reactrouter.com/docs/en/v6/api#generatepath">generatePath</a></li><li><a href="https://reactrouter.com/docs/en/v6/api#location">Location</a></li><li><a href="https://reactrouter.com/docs/en/v6/api#matchroutes">matchRoutes</a></li><li><a href="https://reactrouter.com/docs/en/v6/api#rendermatches">renderMatches</a></li><li><a href="https://reactrouter.com/docs/en/v6/api#matchpath">matchPath</a></li><li><a href="https://reactrouter.com/docs/en/v6/api#resolvepath">resolvePath</a></li><li><a href="https://reactrouter.com/docs/en/v6/api#createsearchparams">createSearchParams</a></li></ul></blockquote><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>[1] <em>react router官网 <a href="https://reactrouter.com/docs/en/v6/api">https://reactrouter.com/docs/en/v6/api</a></em></p>]]></content>
<summary type="html">react router默认版本已经是v6版本了,你还不准备学习?</summary>
<category term="react-router" scheme="https://www.yxlazy.xyz/categories/react-router/"/>
<category term="react" scheme="https://www.yxlazy.xyz/tags/react/"/>
<category term="react-router" scheme="https://www.yxlazy.xyz/tags/react-router/"/>
</entry>
<entry>
<title>如何实现图片懒加载</title>
<link href="https://www.yxlazy.xyz/2022/01/08/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD/"/>
<id>https://www.yxlazy.xyz/2022/01/08/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD/</id>
<published>2022-01-08T03:13:28.000Z</published>
<updated>2023-01-12T08:20:42.174Z</updated>
<content type="html"><![CDATA[<p>当用户访问一个图片很多的页面,如果不做任何处理,可能等到图片加载完成用户可能早已经离开页面,所以为了留住我们的用户,我们需要对这一过程做一次优化,我们选择’懒加载’的方式来进行优化。</p><h2 id="何为懒加载?"><a href="#何为懒加载?" class="headerlink" title="何为懒加载?"></a>何为懒加载?</h2><p>所谓懒加载,就是延迟加载。我们只选择用户需要看到的东西进行加载,对于用户看不到的东西,也许永远也不需要加载,如果第一次请求就加载所有,不仅浪费带宽,而且可能还会逼走你的用户。</p><h2 id="视口(viewpoint)"><a href="#视口(viewpoint)" class="headerlink" title="视口(viewpoint)"></a>视口(viewpoint)</h2><p>说到懒加载,难免不会提到视口,视口又分为<strong>视觉视口(visual viewport)</strong>和<strong>布局视口(layout viewport)</strong>。视口在浏览器中是指我们正在浏览的一部分,即排除浏览器UI和菜单栏的部分。</p><p><code>visual viewport</code> 指当前浏览器中可见的部分,并且可以变化。</p><p><code>layout viewport</code>是指 <code>innerHeight</code> 和 <code>innerWidth</code> 所组成的区域。</p><p>获取视口的宽和高:</p><p><code>document.documentElement.clientWidth</code>: 获取页面对象的宽度(注:<code>Element.clientWidth</code>获得的元素宽度为<code>content + padding * 2</code> 但不包含滚动条)</p><p><code>document.documentElement.clientHeight</code>: 获取页面对象的高度</p><p><code>window.innerWidth</code>: 获取浏览器窗口(<code>viewpoint</code>)宽度,包括滚动条</p><p><code>window.innerHeight</code>: 获取浏览器窗口(<code>viewpoint</code>)高度</p><p><code>window.outerWidth</code>: 是指包括了浏览器外边框的窗口宽度 </p><p><code>window.outerHeight</code>: 获取整个浏览器窗口的高度(单位:像素),包括侧边栏(如果存在)、窗口镶边(<code>window chrome</code>)和窗口调正边框</p><h2 id="Element-getBoundingClientRect"><a href="#Element-getBoundingClientRect" class="headerlink" title="Element.getBoundingClientRect()"></a>Element.getBoundingClientRect()</h2><p>该方法用于返回某个<code>ELement</code>的宽度,高度以及相对于视口的位置。它的返回值是个<code>DOMRect</code>对象,其中包含的属性有:</p><p><code>DOMRect.x</code>: 指Element的水平坐标</p><p><code>DOMRect.y</code>: 指Element的垂直坐标</p><p><code>DOMRect.top</code>: 指Element上边缘距离视口顶部的高度</p><p><code>DOMRect.bottom</code>: 指Element下边缘距离视口顶部的高度</p><p><code>DOMRect.left</code>: 指Element左边缘距离视口左边的宽度</p><p><code>DOMRect.right</code>: 指Element右边缘距离视口左边的宽度</p><p><code>DOMRect.width</code>: 指Element本身的宽度</p><p><code>DOMRect.height</code>: 指Element本身的高度</p><p><img src="/2022/01/08/%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD/E48030B8-4561-4E91-82BC-D94D88C8BA99.png" alt="rect 图片"></p><p>如果是标准盒子模型,元素的尺寸等于<code>width/height + padding + border-width</code>的总和。如果<code>box-sizing: border-box</code>,元素的的尺寸等于 <code>width/height</code>。</p><h2 id="实现懒加载"><a href="#实现懒加载" class="headerlink" title="实现懒加载"></a>实现懒加载</h2><p>接下来我们来实现一个简单的图片懒加载组件</p><p><a href="https://codepen.io/yanxiaolazy/embed/RwZXZPw">https://codepen.io/yanxiaolazy/embed/RwZXZPw</a></p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs jsx"><span class="hljs-title class_">React</span>.<span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-keyword">const</span> node = ref.<span class="hljs-property">current</span><br> <span class="hljs-keyword">if</span> (node) {<br> <span class="hljs-title function_">checkVisible</span>(node, setVisible);<br> }<br>}, []);<br></code></pre></td></tr></table></figure><p>首次渲染时,加载已经出现在视口中的节点</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs jsx"><span class="hljs-title class_">React</span>.<span class="hljs-title function_">useEffect</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-keyword">const</span> node = ref.<span class="hljs-property">current</span><br> <br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">lazyLoadHandle</span> = (<span class="hljs-params"></span>) => {<br> <span class="hljs-title function_">checkVisible</span>(node, setVisible);<br> }<br> <br> <span class="hljs-keyword">if</span> (node) {<br> <span class="hljs-variable language_">window</span>.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'scroll'</span>, lazyLoadHandle)<br> }<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-function">() =></span> <span class="hljs-variable language_">window</span>.<span class="hljs-title function_">removeEventListener</span>(<span class="hljs-string">'scroll'</span>, lazyLoadHandle);<br>}, [])<br></code></pre></td></tr></table></figure><p>首次渲染时绑定滚动事件,用于加载滚动时出现在视口中的节点</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs jsx"><span class="hljs-keyword">const</span> <span class="hljs-title function_">checkVisible</span> = (<span class="hljs-params">node, callback</span>) => {<br> <span class="hljs-keyword">const</span> {top, height} = node.<span class="hljs-title function_">getBoundingClientRect</span>();<br> <span class="hljs-keyword">const</span> innerHeight = <span class="hljs-variable language_">window</span>.<span class="hljs-property">innerHeight</span>;<br> top <= innerHeight && (top + height) > <span class="hljs-number">0</span> && <span class="hljs-title function_">callback</span>(<span class="hljs-literal">true</span>);<br>}<br></code></pre></td></tr></table></figure><p><code>checkVisible</code>函数用于判断节点是否已经出现在视口中,其中<code>top <= innerHeight</code> 说明该节点出现在了视口中,<code>(top + height) > 0</code>说明该节点还未离开视口,接着设置<code>visible</code>为<code>true</code>加载该节点</p>]]></content>
<summary type="html">当用户访问一个图片很多的页面,如果不做任何处理,可能等到图片加载完成用户可能早已经离开页面,所以为了留住我们的用户,我们需要对这一过程做一次优化...</summary>
<category term="javascript" scheme="https://www.yxlazy.xyz/categories/javascript/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
<category term="性能优化" scheme="https://www.yxlazy.xyz/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/"/>
</entry>
<entry>
<title>react-router 简单介绍与本地调试环境搭建</title>
<link href="https://www.yxlazy.xyz/2021/09/29/react-router%E6%9C%AC%E5%9C%B0%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/"/>
<id>https://www.yxlazy.xyz/2021/09/29/react-router%E6%9C%AC%E5%9C%B0%E8%B0%83%E8%AF%95%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA/</id>
<published>2021-09-29T15:23:01.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><code>react-router</code>是<code>react</code>的浏览器路由组件,使用<code>react-router</code>可以轻松的实现浏览器路由跳转。<code>react-router</code>目前已经更新到<code>v5.3.0</code>, 最新的<code>history</code>也已经更新到<code>v5.0.1</code>。目前,从<code>Github</code>仓库可以看出,官方人员正在全力使用<code>TypeScript</code>重构两个项目, 新版的<code>history</code>仓库和原来的仓库并不兼容。我们从<code>npm</code>上直接添加的<code>history</code>仍然是<code>v4</code>版本。</p><blockquote><p>使用<code>TypeScript</code>重构的两个仓库:<br><a href="https://github.com/remix-run/react-router/tree/dev">react-router仓库</a><br><a href="https://github.com/remix-run/history/tree/main">history仓库</a></p></blockquote><blockquote><p>以下介绍的项目版本:<code>react-router</code>是<code>v5.3.0</code>, <code>history</code>是<code>v4.10.1</code> </p></blockquote><h2 id="react-router"><a href="#react-router" class="headerlink" title="react-router"></a>react-router</h2><p><code>react-router</code>仓库, 分为四个包: </p><ul><li><code>react-router</code>: <code>react router</code>的核心组件</li><li><code>react-router-dom</code>: 浏览器路由组件</li><li><code>react-router-native</code>: <code>react native</code>的路由组件</li><li><code>react-router-config</code>: 静态路由组件(如果需要使用<code>react router</code>的静态路由,需要引用这个包,重构后这个包,已经被删除)</li></ul><p>在使用过程中,我们只需要添加对应的包即可,不需要添加<code>react-router</code>,例如,在浏览器中使用添加<code>react-router-dom</code>;在<code>native</code>中使用添加<code>react-router-native</code>;如果需要使用以前的静态路由,就添加<code>react-router-config</code> 。</p><h2 id="本地调试环境搭建"><a href="#本地调试环境搭建" class="headerlink" title="本地调试环境搭建"></a>本地调试环境搭建</h2><p>在阅读源码过程中难免会有令人头痛的代码片段部分,而这部分,想要弄明白,最好的方式就是通过调试,但怎么在本地调试代码呢?我们可以借助<code>webpack</code>的<code>resolve.alias</code>进行本地代码映射:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-attr">resolve</span>: {<br> <span class="hljs-attr">extensions</span>: [<span class="hljs-string">'.js'</span>, <span class="hljs-string">'.jsx'</span>],<br> <span class="hljs-attr">alias</span>: {<br> <span class="hljs-string">'react-router-dom'</span>:path.<span class="hljs-title function_">resolve</span>(__dirname, <span class="hljs-string">'../src/react-router/packages/react-router-dom/modules'</span>),<br> <span class="hljs-string">'react-router'</span>: path.<span class="hljs-title function_">resolve</span>(__dirname, <span class="hljs-string">'../src/react-router/packages/react-router/modules'</span>),<br> <span class="hljs-string">'history'</span>: path.<span class="hljs-title function_">resolve</span>(__dirname, <span class="hljs-string">'../src/history/modules'</span>)<br> }<br>}<br></code></pre></td></tr></table></figure><p>除此之外,<code>react-router</code>还需要其他包,因此需要在<code>react-router</code>和<code>history</code>下运行<code>yarn</code>安装对应的包。</p><p>然后,就可以引入到项目中了:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs jsx"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">BrowserRouter</span> <span class="hljs-keyword">as</span> <span class="hljs-title class_">Router</span>, <span class="hljs-title class_">Switch</span>, <span class="hljs-title class_">Route</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;<br><span class="hljs-keyword">function</span> <span class="hljs-title function_">Start</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">return</span> (<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>test page<span class="hljs-tag"></<span class="hljs-name">p</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br> );<br>}<br><span class="hljs-keyword">const</span> <span class="hljs-title function_">Root</span> = (<span class="hljs-params"></span>) => {<br> <span class="hljs-keyword">return</span>(<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>root page<span class="hljs-tag"></<span class="hljs-name">p</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br> )<br>}<br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">return</span>(<br> <span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">Router</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Switch</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/test'</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Start}/</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Root}</span> /></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">Switch</span>></span></span><br><span class="language-xml"> <span class="hljs-tag"></<span class="hljs-name">Router</span>></span></span><br> )<br>}<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">App</span>;<br></code></pre></td></tr></table></figure>]]></content>
<summary type="html">react-router是react标配的路由组件,现在为了能更好的研究它,我们先搭建一个本地调试环境用于后续的研究吧</summary>
<category term="react-router" scheme="https://www.yxlazy.xyz/categories/react-router/"/>
<category term="react" scheme="https://www.yxlazy.xyz/tags/react/"/>
<category term="源码解析" scheme="https://www.yxlazy.xyz/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
<category term="react-router" scheme="https://www.yxlazy.xyz/tags/react-router/"/>
</entry>
<entry>
<title>React Context</title>
<link href="https://www.yxlazy.xyz/2021/09/23/React%E4%B9%8BContext/"/>
<id>https://www.yxlazy.xyz/2021/09/23/React%E4%B9%8BContext/</id>
<published>2021-09-23T14:40:26.000Z</published>
<updated>2023-01-12T08:20:42.150Z</updated>
<content type="html"><![CDATA[<p><img src="/2021/09/23/React%E4%B9%8BContext/image-20210924142001161.png"></p><hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><code>React</code>为我们提供了<code>Context</code>用于将数据进行广播,消费者只需使用特定的方法就能拿到广播的数据,不用再采用层层递传<code>props</code>的方式。如果组件之间需要共享数据,也可以采用<code>Context</code>的方式,如<code>react-router</code>中的<code>Router</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><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs react">...<br>render() {<br> return (<br> <RouterContext.Provider<br> value={{<br> history: this.props.history,<br> location: this.state.location,<br> match: Router.computeRootMatch(this.state.location.pathname),<br> staticContext: this.props.staticContext<br> }}<br> ><br> <HistoryContext.Provider<br> children={this.props.children || null}<br> value={this.props.history}<br> /><br> </RouterContext.Provider><br> );<br> }<br>...<br></code></pre></td></tr></table></figure><p>使用<code>Provider</code>包裹组件后,后续就能直接从<code>context</code>中获取最新的<code>history</code>等数据。</p><blockquote><p>在什么情况下需要使用<code>Context</code> : <em><a href="https://react.docschina.org/docs/context.html#when-to-use-context">https://react.docschina.org/docs/context.html#when-to-use-context</a></em> </p><p>使用之前的考虑: <em><a href="https://react.docschina.org/docs/context.html#before-you-use-context">https://react.docschina.org/docs/context.html#before-you-use-context</a></em> </p></blockquote><h2 id="React-createContext"><a href="#React-createContext" class="headerlink" title="React.createContext"></a>React.createContext</h2><p>用于创建一个<code>Context</code>对象,其中包含<code>Provider</code>和<code>Consumer</code>两个组件。<code>Provider</code>组件可以提供用于<code>Consumer</code>组件消费的<code>context</code>。同时可以在创建<code>Context</code>对象时,为其指定一个默认值:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs react">const NewContext = React.createContext(defaultValue);<br></code></pre></td></tr></table></figure><p>如果<code>Consumer</code>组件不能在所处的树中匹配到<code>Provider</code>,将会使用这个<code>defaultValue</code>。</p><blockquote><p>如果直接给<code>Provider</code>的<code>value</code>赋值为<code>undefined</code>,消费组件的<code>defaultValue</code>不会生效</p></blockquote><h2 id="Context-Provider"><a href="#Context-Provider" class="headerlink" title="Context.Provider"></a>Context.Provider</h2><p><code>Provider</code>可以为消费组件提供<code>context</code>,通过将要广播的数据放入<code>value</code>,内部的消费组件就可以消费这个<code>value</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></pre></td><td class="code"><pre><code class="hljs react">const {Provider} = NewContext;<br>...<br><Provider value='provider'><br><Button /><br></Provider><br>...<br></code></pre></td></tr></table></figure><p><code>Provider</code>可以进行嵌套,但<code>Consumer</code>只会消费<strong>最近</strong>的<code>Provider</code>。</p><p>如果<code>Provider</code>的<code>value</code>发生变化,<strong>它内部的所有的消费组件都会重新渲染</strong>,即使使用了<code>shouldComponentUpdate</code>进行了优化处理,也不会影像消费组件的重新渲染。</p><blockquote><p>如果为<code>Provider</code>提供的是对象,由于该父组件进行重渲染时,每次<code>value</code>都会得到一个新的对象,从而consumers组件中也会发生不必要的渲染。</p></blockquote><p>解决方式是将该对象保存在<code>state</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></pre></td><td class="code"><pre><code class="hljs react">const App = () => {<br> const [state, setState] = useState({});<br> useEffect(() => {<br> ...<br> setState({theme: '#fff'})<br> ...<br> })<br> return(<br> <NewContext.Provider value={state}><br> <Button /> <br> </NewContext.Provider>)<br>}<br></code></pre></td></tr></table></figure><h2 id="Class-contextType"><a href="#Class-contextType" class="headerlink" title="Class.contextType"></a>Class.contextType</h2><p>通过给组件的<code>contextType</code>属性赋值为<code>Context</code>对象后,可以在该组件的内部拿到<code>context</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></pre></td><td class="code"><pre><code class="hljs react">class App extends React.Component {<br> ...<br> componentDidMount() {<br> ...<br> let value = this.context<br> ...<br> }<br> ...<br> render() {<br> ...<br> let value = this.context<br> ...<br> }<br>}<br>App.contextType = NewContext<br></code></pre></td></tr></table></figure><p>可以在组件的任何生命周期中拿到<code>context</code>。也可以使用<code>static</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></pre></td><td class="code"><pre><code class="hljs react">class App extends React.Component {<br> static contextType = NewContext<br>render() {<br> ...<br> let value = this.context<br> ...<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="Context-Consumer"><a href="#Context-Consumer" class="headerlink" title="Context.Consumer"></a>Context.Consumer</h2><p><code>contextType</code>只适用于类组件,对于函数组件,可以使用<code>Consutmer</code>组件获取<code>context</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></pre></td><td class="code"><pre><code class="hljs react">const App = () => {<br> return(<br> <NewContext.Consumer><br> {context => {<br> // 拿到context,并进行其他逻辑<br> <br> // 返回React节点<br> return <Button style={{background: context.theme}}/><br> }}<br> </NewContext.Consumer><br> )<br>}<br></code></pre></td></tr></table></figure><p><code>Consumer</code>组件中需要一个函数来接收当前的<code>context</code>,函数返回一个<code>React</code>节点。</p><h2 id="Context-displayName"><a href="#Context-displayName" class="headerlink" title="Context.displayName"></a>Context.displayName</h2><p><code>Context</code>对象接收一个<code>displayName</code>属性,类型为字符串,React DevTools 使用该字符串来确定 <code>context</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><code class="hljs react">const MyContext = React.createContext(/* some value */);<br>MyContext.displayName = 'MyDisplayName';<br><br><MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中<br><MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中<br></code></pre></td></tr></table></figure><h2 id="useContext"><a href="#useContext" class="headerlink" title="useContext"></a>useContext</h2><p><code>useContext</code>是<code>React</code>提供的一个<code>hooks API</code>,可以用来拿到对应的<code>Context</code>对象提供的<code>context</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><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs react">// context.js<br>export const NewContext = React.createContext(defaultValue);<br><br>// app.js<br>import {NewContext} from './context'<br>import {Buttons} from './buttons'<br>const App = () => {<br> ...<br> return(<br> <NewContext.Provider value={{theme: '#fff'}}><br> ...<br> <Buttons /><br> </NewContext.Provider><br> )<br>}<br><br>// buttons.js<br>import {Button} from './button'<br>const Buttons = () => {<br> ...<br> return(<br> ...<br> <Button /><br> ...<br> )<br>}<br><br>// button.js<br>import {useContext} from 'react'<br>import {NewContext} from './context'<br>const Button = () => {<br> const context = useContext(NewContext)<br> <br> return (<br> <button style={{background: context.theme}}>按钮</button><br> )<br>}<br></code></pre></td></tr></table></figure><p>当组件上层最近的 <code><NewContext.Provider></code> 更新时,该 Hook 会触发重渲染,并使用最新传递给 <code>NewContext</code> provider 的 context <code>value</code> 值。即使祖先使用 <a href="https://react.docschina.org/docs/react-api.html#reactmemo"><code>React.memo</code></a> 或 <a href="https://react.docschina.org/docs/react-component.html#shouldcomponentupdate"><code>shouldComponentUpdate</code></a>,也会在组件本身使用 <code>useContext</code> 时重新渲染。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>[1] <em><a href="https://react.docschina.org/docs/context.html">https://react.docschina.org/docs/context.html</a></em></p><p>[2] <em><a href="https://react.docschina.org/docs/hooks-reference.html#usecontext">https://react.docschina.org/docs/hooks-reference.html#usecontext</a></em> </p>]]></content>
<summary type="html">React为我们提供了Context用于将数据进行广播,消费组件只需使用特定的方法就能拿到广播的数据,不用再采用层层递传的方式...</summary>
<category term="react" scheme="https://www.yxlazy.xyz/categories/react/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
<category term="react" scheme="https://www.yxlazy.xyz/tags/react/"/>
</entry>
<entry>
<title>FormData多文件上传并同时添加其他数据</title>
<link href="https://www.yxlazy.xyz/2021/09/10/FormData%E5%A4%9A%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E5%B9%B6%E5%90%8C%E6%97%B6%E6%B7%BB%E5%8A%A0%E5%85%B6%E4%BB%96%E6%95%B0%E6%8D%AE/"/>
<id>https://www.yxlazy.xyz/2021/09/10/FormData%E5%A4%9A%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E5%B9%B6%E5%90%8C%E6%97%B6%E6%B7%BB%E5%8A%A0%E5%85%B6%E4%BB%96%E6%95%B0%E6%8D%AE/</id>
<published>2021-09-10T03:14:05.000Z</published>
<updated>2023-01-12T08:20:42.150Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在做项目时,有一个接口要求上传文件的同时能携带其它数据,在这里使用的是<code>FormData</code>对象来进行操作。简单介绍一下这个对象。<br><code>FormData</code> 接口提供了一种表示表单数据的键值对 <code>key/value</code> 的构造方式,并且可以轻松的将数据通过<code>XMLHttpRequest.send()</code> 方法发送出去,本接口和此方法都相当简单直接。如果送出时的编码类型被设为 <code>"multipart/form-data"</code>,它会使用和表单一样的格式。(以上出自<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/FormData">这里</a>) </p><h2 id="单文件上传"><a href="#单文件上传" class="headerlink" title="单文件上传"></a>单文件上传</h2><p>这里使用<code>FormData</code>的<code>append()</code>方法,该方法会向<code>FormData</code>对象中添加新的属性值</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'file'</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'file'</span>/></span><br><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'upload'</span>></span>上传<span class="hljs-tag"></<span class="hljs-name">button</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="language-javascript"></span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> input = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'file'</span>);</span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> uploadBtn = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'upload'</span>);</span><br><span class="language-javascript"> <span class="hljs-keyword">let</span> files = <span class="hljs-literal">null</span>;</span><br><span class="language-javascript"></span><br><span class="language-javascript"> input.<span class="hljs-property">onchange</span> = <span class="hljs-function">(<span class="hljs-params">e</span>) =></span> {</span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> file = input.<span class="hljs-property">files</span>[<span class="hljs-number">0</span>];</span><br><span class="language-javascript"> files = file;</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"></span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> <span class="hljs-title function_">uploadFile</span> = (<span class="hljs-params">file</span>) => {</span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FormData</span>();</span><br><span class="language-javascript"> formData.<span class="hljs-title function_">append</span>(<span class="hljs-string">'file'</span>, file);</span><br><span class="language-javascript"> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">'https://xxx.com'</span>, {</span><br><span class="language-javascript"> <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, </span><br><span class="language-javascript"> <span class="hljs-attr">body</span>: formData</span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">res</span> =></span> {</span><br><span class="language-javascript"> <span class="hljs-comment">//在这里处理请求成功</span></span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> .<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">err</span> =></span> {</span><br><span class="language-javascript"> <span class="hljs-comment">//在这里处理请求失败</span></span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"> uploadBtn.<span class="hljs-property">onclick</span> = <span class="hljs-function">() =></span> {</span><br><span class="language-javascript"> <span class="hljs-keyword">if</span> (!files) {</span><br><span class="language-javascript"> <span class="hljs-title function_">alert</span>(<span class="hljs-string">'请选择文件'</span>)</span><br><span class="language-javascript"> <span class="hljs-keyword">return</span></span><br><span class="language-javascript"> }</span><br><span class="language-javascript"> <span class="hljs-title function_">uploadFile</span>(files);</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>注:上传文件是不需要我们自己设置<code>Content-Type</code>头的,设置了反而无法上传,切记,切记,切记!!!</p><p>详细的可以查看这个issue: <a href="https://github.com/github/fetch/issues/505#issuecomment-293064470">https://github.com/github/fetch/issues/505#issuecomment-293064470</a></p></blockquote><h3 id="多文件上传"><a href="#多文件上传" class="headerlink" title="多文件上传"></a>多文件上传</h3><p>如果<code>FormData</code>的属性值中已经存在对应的属性值也不会被覆盖,而是会将这个新值添加到已有集合的后面</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs html">+ <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'file'</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'file'</span> <span class="hljs-attr">multiple</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'upload'</span>></span>上传<span class="hljs-tag"></<span class="hljs-name">button</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="language-javascript"></span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> input = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'file'</span>);</span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> uploadBtn = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'upload'</span>);</span><br><span class="language-javascript"> <span class="hljs-keyword">let</span> files = <span class="hljs-literal">null</span>;</span><br><span class="language-javascript"></span><br><span class="language-javascript"> input.<span class="hljs-property">onchange</span> = <span class="hljs-function">(<span class="hljs-params">e</span>) =></span> {</span><br><span class="language-javascript">- <span class="hljs-keyword">const</span> file = input.<span class="hljs-property">files</span>[<span class="hljs-number">0</span>];</span><br><span class="language-javascript">- files = file;</span><br><span class="language-javascript">+ <span class="hljs-keyword">const</span> files = input.<span class="hljs-property">files</span></span><br><span class="language-javascript">+ files = files;</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"></span><br><span class="language-javascript">- <span class="hljs-keyword">const</span> <span class="hljs-title function_">uploadFile</span> = (<span class="hljs-params">file</span>) => {</span><br><span class="language-javascript">+ <span class="hljs-keyword">const</span> <span class="hljs-title function_">uploadFile</span> = (<span class="hljs-params">files</span>) => {</span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FormData</span>();</span><br><span class="language-javascript">- formData.<span class="hljs-title function_">append</span>(<span class="hljs-string">'file'</span>, file);</span><br><span class="language-javascript"><span class="hljs-comment">//多文件上传,formData的append方法,对于同一个key会将新添加的key/value添加到后面,不会覆盖已经存在的key</span></span><br><span class="language-javascript">+ <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> file <span class="hljs-keyword">of</span> files) {</span><br><span class="language-javascript">+ formData.<span class="hljs-title function_">append</span>(<span class="hljs-string">'files'</span>, file);</span><br><span class="language-javascript">+ } </span><br><span class="language-javascript"> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">'https://xxx.com'</span>, {</span><br><span class="language-javascript"> <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, </span><br><span class="language-javascript"> <span class="hljs-attr">body</span>: formData</span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">res</span> =></span> {</span><br><span class="language-javascript"> <span class="hljs-comment">//在这里处理请求成功</span></span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> .<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">err</span> =></span> {</span><br><span class="language-javascript"> <span class="hljs-comment">//在这里处理请求失败</span></span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"> uploadBtn.<span class="hljs-property">onclick</span> = <span class="hljs-function">() =></span> {</span><br><span class="language-javascript"> <span class="hljs-keyword">if</span> (!files) {</span><br><span class="language-javascript"> <span class="hljs-title function_">alert</span>(<span class="hljs-string">'请选择文件'</span>)</span><br><span class="language-javascript"> <span class="hljs-keyword">return</span></span><br><span class="language-javascript"> }</span><br><span class="language-javascript"> <span class="hljs-title function_">uploadFile</span>(files);</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"> </span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><code class="hljs html"> <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'file'</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'file'</span> <span class="hljs-attr">multiple</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">'upload'</span>></span>上传<span class="hljs-tag"></<span class="hljs-name">button</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="language-javascript"></span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> input = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'file'</span>);</span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> uploadBtn = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'upload'</span>);</span><br><span class="language-javascript"> <span class="hljs-keyword">let</span> files = <span class="hljs-literal">null</span>;</span><br><span class="language-javascript"></span><br><span class="language-javascript"> input.<span class="hljs-property">onchange</span> = <span class="hljs-function">(<span class="hljs-params">e</span>) =></span> {</span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> files = input.<span class="hljs-property">files</span></span><br><span class="language-javascript"> files = files;</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"></span><br><span class="language-javascript">- <span class="hljs-keyword">const</span> <span class="hljs-title function_">uploadFile</span> = (<span class="hljs-params">files</span>) => {</span><br><span class="language-javascript">+ <span class="hljs-keyword">const</span> <span class="hljs-title function_">uploadFile</span> = (<span class="hljs-params">files, payload</span>) => {</span><br><span class="language-javascript"> <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FormData</span>();</span><br><span class="language-javascript"> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> file <span class="hljs-keyword">of</span> files) {</span><br><span class="language-javascript"> formData.<span class="hljs-title function_">append</span>(<span class="hljs-string">'files'</span>, file);</span><br><span class="language-javascript"> } </span><br><span class="language-javascript"> <span class="hljs-comment">//对于其他附加的数据,分别增加到FormData中即可</span></span><br><span class="language-javascript">+ <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> key <span class="hljs-keyword">in</span> payload) {</span><br><span class="language-javascript">+ formData.<span class="hljs-title function_">append</span>([key], payload[key]);</span><br><span class="language-javascript">+ }</span><br><span class="language-javascript"> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">'https://xxx.com'</span>, {</span><br><span class="language-javascript"> <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, </span><br><span class="language-javascript"> <span class="hljs-attr">body</span>: formData</span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> .<span class="hljs-title function_">then</span>(<span class="hljs-function"><span class="hljs-params">res</span> =></span> {</span><br><span class="language-javascript"> <span class="hljs-comment">//在这里处理请求成功</span></span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> .<span class="hljs-title function_">catch</span>(<span class="hljs-function"><span class="hljs-params">err</span> =></span> {</span><br><span class="language-javascript"> <span class="hljs-comment">//在这里处理请求失败</span></span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"> uploadBtn.<span class="hljs-property">onclick</span> = <span class="hljs-function">() =></span> {</span><br><span class="language-javascript"> <span class="hljs-keyword">if</span> (!files) {</span><br><span class="language-javascript"> <span class="hljs-title function_">alert</span>(<span class="hljs-string">'请选择文件'</span>)</span><br><span class="language-javascript"> <span class="hljs-keyword">return</span></span><br><span class="language-javascript"> }</span><br><span class="language-javascript">- <span class="hljs-title function_">uploadFile</span>(files);</span><br><span class="language-javascript">+ <span class="hljs-title function_">uploadFile</span>(files, {<span class="hljs-attr">id</span>: <span class="hljs-string">'upload'</span>});</span><br><span class="language-javascript"> }</span><br><span class="language-javascript"> </span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><blockquote><p><code>FormData</code>对象的<code>set()</code>方法和<code>append()</code>方法比较,<code>set()</code>方法指定的键如果存在,会使用新值覆盖原来的值,而<code>append()</code>方法会把新值添加到已有值集合的后面</p></blockquote><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>[1] <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/FormData">https://developer.mozilla.org/zh-CN/docs/Web/API/FormData</a></p>]]></content>
<summary type="html">如何使用FormData实现文件上传呢?单文件上传和多文件上传呢?</summary>
<category term="javascript" scheme="https://www.yxlazy.xyz/categories/javascript/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
</entry>
<entry>
<title>git 用法</title>
<link href="https://www.yxlazy.xyz/2021/07/15/git-%E7%94%A8%E6%B3%95/"/>
<id>https://www.yxlazy.xyz/2021/07/15/git-%E7%94%A8%E6%B3%95/</id>
<published>2021-07-15T14:59:22.000Z</published>
<updated>2023-01-12T08:20:42.158Z</updated>
<content type="html"><![CDATA[<p><img src="/2021/07/15/git-%E7%94%A8%E6%B3%95/github.jpg"></p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>别人上班,我摸鱼,别人加班,我摸鱼,然后就写出了这篇 Git 的简单使用教程。</p><p>摸摸索索一个多月总算是完成了这篇 git 的学习笔记 😂, 还好最后还是坚持了下来,可能有些有问题,欢迎各位大佬指正,可以通过提<a href="https://github.com/yxlazy/blog/issues">issue</a>的方式,或者关注我的公众号, 微信搜索 <strong>前端入坑手册</strong>,在后台给我留言。</p><h2 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h2><ol><li>首先需要生成 ssh 秘钥,<code>ssh-keygen -t rsa -C "你公司内部邮箱地址"</code> ,如果成功,将会在<code>~/.ssh</code>文件夹下生成对应的私钥和公钥。<br><code>ls</code></li></ol><p><img src="/2021/07/15/git-%E7%94%A8%E6%B3%95/image-20210716095654977.png"></p><ol start="2"><li>复制<code>id_rsa.pub</code>文件的内容(如果是 GitHub)到<code>setting -> SSH and GPG keys-> New SSH key</code> 并保存</li></ol><blockquote><p>记住放到 GitHub 上的是公钥</p></blockquote><ol start="3"><li><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><code class="hljs bash">git config --global user.name <span class="hljs-string">"xxxx"</span><br>git config --global user.email <span class="hljs-string">"xxxx"</span><br></code></pre></td></tr></table></figure></li></ol><p>完成以上步骤后,就可以愉快的 pull 或 push 代码了。和 https 拉取方式不同的是,https 方式需要每次提交前都手动输入用户名和密码,ssh 的方式配置完毕后 Git 都会使用你本地的私钥和远程仓库的公钥进行验证是否是一对秘钥,从而简化了操作流程。</p><h2 id="基础篇"><a href="#基础篇" class="headerlink" title="基础篇"></a>基础篇</h2><p> 初始化一个 git 仓库,使用命令<code>git init</code> ,会在项目下生成一个.git 的文件夹,这就是 Git 的版本库</p><blockquote><p>前情提要:git 有工作区和暂存区之分</p><p>工作区:就是我们项目所在的区域</p><p>暂存区:git add 操作之后,实际就是将工作区的内容添加到暂存区</p></blockquote><h3 id="git-add"><a href="#git-add" class="headerlink" title="git add"></a>git add</h3><p>将工作区的更改,添加到暂存区</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></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 添加指定文件,后面可跟多个文件,以空格分开</span><br>git add <具体的文件名或某个文件下的所有内容><br><span class="hljs-comment"># 添加项目下的所有文件</span><br>git add .<br></code></pre></td></tr></table></figure><h3 id="git-commit"><a href="#git-commit" class="headerlink" title="git commit"></a>git commit</h3><p>将暂存区的内容提交到当前分支,也就是本地仓库</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><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 会打开一个可编辑区域</span><br>git commit<br><span class="hljs-comment"># 直接将备注添加在后面</span><br>git commit -m <span class="hljs-string">"xxxx"</span><br><span class="hljs-comment"># 等价于git add && git commit</span><br>git commit -am <span class="hljs-string">"xxxx"</span><br><span class="hljs-comment"># 对最近一次的提交信息进行修改,会更改hash值</span><br>git commit --amend<br></code></pre></td></tr></table></figure><h3 id="git-push"><a href="#git-push" class="headerlink" title="git push "></a>git push <remote> <place></place></remote></h3><p>将本地分支发布到远端分支</p><ul><li>常用命令</li></ul><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></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 不带参数的push。注意 —— git push 不带任何参数时的行为与 Git 的一个名为 push.default 的配置有关</span><br>git push<br><span class="hljs-comment"># 将本地的dev分支,发布到远端同名的dev分支</span><br>git push origin dev<br><span class="hljs-comment"># 将本地当前分支,发布到远端与本地当前分支同名的分支上</span><br>git push origin<br></code></pre></td></tr></table></figure><ul><li>进阶命令</li></ul><p>格式:<code>git push origin <source>:<destination></code></p><blockquote><p><code>source</code> 可以是任何 Git 能识别的格式,例如:<code>HEAD~2</code>,<code>foo^2</code></p></blockquote><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><code class="hljs bash"><span class="hljs-comment"># 将本地dev分支上的dev^处发生的更改提交到远端分支feat</span><br>git push origin dev^:feat<br></code></pre></td></tr></table></figure><h3 id="git-pull"><a href="#git-pull" class="headerlink" title="git pull "></a>git pull <remote> <place></place></remote></h3><p>拉取远端更新分支到本地</p><ul><li>常用命令</li></ul><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><code class="hljs bash"><span class="hljs-comment"># 不带参数的pull。 将远端origin上的master上的更新拉取到本地,并与本地的master分支进行合并</span><br>git pull<br>git pull origin<br></code></pre></td></tr></table></figure><ul><li>进阶命令</li></ul><p>同样<code>git pull</code>也可以使用<code><source>:<destination></code>格式<br>格式:<code>git pull origin <source>:<destination></code></p><blockquote><p><code>source</code> 可以是任何 Git 能识别的格式,例如:<code>HEAD~2</code>,<code>foo^2</code></p></blockquote><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><code class="hljs bash"><span class="hljs-comment"># 拉取远端分支上的更新并与本地的某个分支进行合并</span><br>git pull origin master:dev<br></code></pre></td></tr></table></figure><h3 id="git-fetch"><a href="#git-fetch" class="headerlink" title="git fetch"></a>git fetch</h3><p>将远程主机的最新内容拉取到本地,但不会合并</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><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 默认远程主机是origin</span><br>git fetch<br><span class="hljs-comment"># 拉取所有远程主机上的更新,默认是origin</span><br>git fetch --all<br><span class="hljs-comment"># 拉取远程主机origin上dev分支最新的更新</span><br>git fetch origin dev<br></code></pre></td></tr></table></figure><h3 id="git-merge"><a href="#git-merge" class="headerlink" title="git merge"></a>git merge</h3><p>将两个或多个分支进行合并,并生成一个新的提交记录,保留合并历史</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><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 假设在master分支,以下命令,就是在说,将dev分支合并到master上</span><br>git merge dev<br><span class="hljs-comment"># -ff 指fast-forword, 默认采用模式。使用这种模式,不会创建新的commit节点</span><br>git merge --ff -m <span class="hljs-string">"添加一些message"</span><br><span class="hljs-comment"># --no-ff fast-forword模式下,仍会创建一个新的commit节点。这是合并一个tag时的默认行为</span><br>git merge --no-ff -m <span class="hljs-string">"添加message"</span><br></code></pre></td></tr></table></figure><blockquote><p>执行<code>git pull</code>命令,实际就是<code>git fetch + git merge</code></p></blockquote><h3 id="git-rebase"><a href="#git-rebase" class="headerlink" title="git rebase"></a>git rebase</h3><p>是另一个合并分支的命令,其实际上就是取出一系列的提交记录,”复制“它们, 然后在另外一个地方着个的放下去。</p><ul><li><p>合并分支</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><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><code class="hljs bash"><span class="hljs-comment"># 当前分支topic</span><br><span class="hljs-comment"># A---B---C topic</span><br><span class="hljs-comment"># /</span><br><span class="hljs-comment"># D---E---F---G master</span><br><span class="hljs-comment">#执行以下任意一条命令</span><br>git rebase master<br>git rebase master topic<br><span class="hljs-comment"># A'--B'--C' topic</span><br><span class="hljs-comment"># /</span><br><span class="hljs-comment"># D---E---F---G master</span><br></code></pre></td></tr></table></figure></li></ul><ul><li><code>git rebase --abort</code>: <code>rebase</code>时,发生冲突,可以用来阻止<code>rebase</code></li><li><code>git rebase --continue</code>: <code>rebase</code>时, 解决了冲突后,用来继续<code>rebase</code></li><li><code>git rebase -i</code>: 合并多个<code>commit</code> 记录</li><li><code>git pull --rebase = git fetch + git rebase</code></li></ul><h3 id="git-checkout"><a href="#git-checkout" class="headerlink" title="git checkout"></a>git checkout</h3><p>这是一个强大的命令,可以做很多事</p><ul><li><p>创建新的分支</p><p><code>git checkout -b dev</code> : 基于当前分支创建一个新的<code>dev</code>分支,并切换到<code>dev</code>分支</p><p><code>git checkout -b dev master</code>: 基于<code>master</code>分支创建一个新的<code>dev</code>分支,并切换到<code>dev</code>分支</p><blockquote><p>等价于<code>git branch -b dev</code></p></blockquote></li><li><p><code>git checkout dev</code>: 切换分支, 切换到<code>dev</code>分支</p><blockquote><p>等价于<code>git branch dev</code></p></blockquote></li><li><p><code>git checkout <file></code>: (<code>file</code>可以是个”点”, 表示撤销所有文件的更改)撤销工作区中的指定更改,不包含未被<code>git</code>监控的文件</p><blockquote><p>等价于<code>git restore <file> </code></p><p>如果需要撤销暂存区的更改,可以使用<code>git restore --staged <file></code></p></blockquote></li></ul><h3 id="git-cherry-pick"><a href="#git-cherry-pick" class="headerlink" title="git cherry-pick"></a>git cherry-pick</h3><p>如果你只需要某个分支的部分提交代码,就可以考虑使用这条命令</p><ul><li><code>git cherry-pick <commit></code></li></ul><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><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 当前分支 master (以下字母代表commit hash)</span><br><span class="hljs-comment"># e - f - g dev</span><br><span class="hljs-comment"># /</span><br><span class="hljs-comment"># a - b - c - d master</span><br><span class="hljs-comment"># 倘若master分支只需要dev分支上的本分提交, 可以使用以下代码</span><br>git cherry-pick e...f<br><span class="hljs-comment"># e - f - g dev</span><br><span class="hljs-comment"># /</span><br><span class="hljs-comment"># a - b - c - d - e' - f' master</span><br></code></pre></td></tr></table></figure><ul><li><code>git cherry-pick <branch></code> : 命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。</li><li><code>git cherry-pick --abort</code>: 发生代码冲突时,可以阻止合并代码,回到操作前的样子</li><li><code>git cherry-pick --continue</code>: 解决代码冲突后,继续进行<code>cherry-pick</code> 命令</li><li><code>git cherry-pick --quite</code> : 发生代码冲突后,可以阻止合并代码,但是不会回到操作前的样子</li></ul><h2 id="其他命令"><a href="#其他命令" class="headerlink" title="其他命令"></a>其他命令</h2><h3 id="git-branch"><a href="#git-branch" class="headerlink" title="git branch"></a>git branch</h3><p>用于分支管理的命令</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><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 列出所有分支</span><br>git branch<br><span class="hljs-comment"># 列出所有分支,并带上最进一次的commit记录</span><br>git branch -v<br><span class="hljs-comment"># 创建一个新分支dev</span><br>git branch dev<br><span class="hljs-comment"># 删除dev分支</span><br>git branch -d dev<br><span class="hljs-comment"># git branch --delete --force的简写,用于强制删除</span><br>git branch -D dev<br><span class="hljs-comment"># 基于当前分支复制一个新的分支branch-copy</span><br>git branch -c branch-copy<br><span class="hljs-comment"># 基于dev分支复制一个新的分支dev-copy</span><br>git branch -c dev dev-copy<br></code></pre></td></tr></table></figure><h3 id="git-stash"><a href="#git-stash" class="headerlink" title="git stash"></a>git stash</h3><p>将本地的更改暂存。</p><p>当本地发生更改,但还未<code>commit</code>, 又要进行其他操作( 如更新远端最新代码 ),如果直接<code>git pull</code>或<code>git pull --rebase</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><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 将更改暂存起来</span><br>git stash<br><span class="hljs-comment"># 也可以在暂存时,添加备注</span><br>git stash -m <span class="hljs-string">"暂存"</span><br><span class="hljs-comment"># 查看stash了哪些</span><br>git stash list<br><span class="hljs-comment"># 取出最近的一次stash, 并删除记录</span><br>git stash pop<br><span class="hljs-comment"># 取出指定的stash记录, 不会删除</span><br>git stash list<br>git stash apply <前面list 列出的某一项><br><span class="hljs-comment"># 删除所有的stash</span><br>git stash clear<br><span class="hljs-comment"># 删除指定的一次stash</span><br>git stash drop <stash 记录><br></code></pre></td></tr></table></figure><blockquote><p>如果需要暂存还未被<code>git</code>跟踪的文件,可以先<code>git add</code> 后再<code>git stash</code></p></blockquote><h3 id="git-reset"><a href="#git-reset" class="headerlink" title="git reset"></a>git reset</h3><p>版本回退,如果是还未提交到远端仓库,可以直接使用<code>git reset</code> 回退,如果已经提交则需要下面提到的<code>git revert</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><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 回退一个commit</span><br>git reset HEAD^<br><span class="hljs-comment"># 向后回退两个commit, 并且丢失回退后的所有commit数据</span><br>git reset --hard HEAD~2<br></code></pre></td></tr></table></figure><h3 id="git-revert"><a href="#git-revert" class="headerlink" title="git revert"></a>git revert</h3><p>版本回退,会创建一个新的<code>commit</code>记录。</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><code class="hljs bash"><span class="hljs-comment"># 撤销最新的commit</span><br>git revert HEAD<br></code></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>[1] <a href="https://juejin.cn/post/6974184935804534815?share_token=e6867a4f-d921-469d-84e2-e4a3e1810d0f">https://juejin.cn/post/6974184935804534815?share_token=e6867a4f-d921-469d-84e2-e4a3e1810d0f</a></p><p>[2] <a href="https://learngitbranching.js.org/?locale=zh_CN">https://learngitbranching.js.org/?locale=zh_CN</a></p><p>[3] <a href="https://www.liaoxuefeng.com/wiki/896043488029600">https://www.liaoxuefeng.com/wiki/896043488029600</a>)</p><p>[4] <a href="https://www.cnblogs.com/hujunzheng/p/9732936.html">https://www.cnblogs.com/hujunzheng/p/9732936.html</a></p><p>[5] <a href="https://www.ruanyifeng.com/blog/2020/04/git-cherry-pick.html">https://www.ruanyifeng.com/blog/2020/04/git-cherry-pick.html</a></p>]]></content>
<summary type="html">新人入职不知怎么拉取仓库代码?不知道git fetch 和 git pull的区别? 到底是用git merge还是git rebase? 这里有你想要的答案</summary>
<category term="other" scheme="https://www.yxlazy.xyz/categories/other/"/>
<category term="git" scheme="https://www.yxlazy.xyz/tags/git/"/>
</entry>
<entry>
<title>typeof的用法</title>
<link href="https://www.yxlazy.xyz/2021/06/30/typeof%E7%9A%84%E7%94%A8%E6%B3%95/"/>
<id>https://www.yxlazy.xyz/2021/06/30/typeof%E7%9A%84%E7%94%A8%E6%B3%95/</id>
<published>2021-06-30T08:11:59.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><code>typeof</code> 的一般操作,就是用来判断某个变量是什么类型,从而进行下一步操作。但是最近发现<code>lodash</code>源码中<code>typeof</code>还有如此作用,特记录于此</p><h2 id="常规操作"><a href="#常规操作" class="headerlink" title="常规操作"></a>常规操作</h2><p>常规操作无非就是判断类型,如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">typeof</span> <span class="hljs-literal">null</span> === <span class="hljs-string">'object'</span> <span class="hljs-comment">//true 注:特殊</span><br><span class="hljs-keyword">typeof</span> <span class="hljs-literal">undefined</span> === <span class="hljs-string">'undefined'</span> <span class="hljs-comment">//true</span><br><span class="hljs-keyword">typeof</span> <span class="hljs-number">111</span> === <span class="hljs-string">'number'</span> <span class="hljs-comment">//true</span><br><span class="hljs-keyword">typeof</span> <span class="hljs-string">'字符串'</span> === <span class="hljs-string">'string'</span> <span class="hljs-comment">//true</span><br><span class="hljs-keyword">typeof</span> <span class="hljs-literal">true</span> === <span class="hljs-string">'boolean'</span> <span class="hljs-comment">//true</span><br><span class="hljs-keyword">typeof</span> <span class="hljs-title class_">Symbol</span>(<span class="hljs-string">'foo'</span>) === <span class="hljs-string">'symbol'</span> <span class="hljs-comment">//true</span><br><span class="hljs-keyword">typeof</span> <span class="hljs-number">111n</span> === <span class="hljs-string">'bigint'</span> <span class="hljs-comment">//true</span><br><span class="hljs-keyword">typeof</span> {} === <span class="hljs-string">'object'</span> <span class="hljs-comment">//true</span><br><span class="hljs-keyword">typeof</span> [] === <span class="hljs-string">'object'</span> <span class="hljs-comment">//true</span><br><span class="hljs-keyword">typeof</span> (<span class="hljs-function">() =></span> {}) === <span class="hljs-string">'function'</span> <span class="hljs-comment">//true</span><br></code></pre></td></tr></table></figure><h2 id="非常规操作"><a href="#非常规操作" class="headerlink" title="非常规操作"></a>非常规操作</h2><p>我们知道,对于未声明的变量,只能执行一个操作,那就是使用<code>typeof</code>判断类型</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//let a = 1 //确保a没有声明</span><br><span class="hljs-keyword">typeof</span> a <span class="hljs-comment">//undefined</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(a) <span class="hljs-comment">//报错: Uncaught ReferenceError: a is not defined</span><br></code></pre></td></tr></table></figure><p>那怎么能让第二个语句不报错,有输出呢?往下看</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-keyword">typeof</span> a !== <span class="hljs-string">'undefined'</span> && a) <span class="hljs-comment">//输出false</span><br></code></pre></td></tr></table></figure><blockquote><p><code>lodash</code>类似源码</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">/** Detect free variable `global` from Node.js. */</span><br><span class="hljs-keyword">const</span> freeGlobal = <span class="hljs-keyword">typeof</span> <span class="hljs-variable language_">global</span> === <span class="hljs-string">'object'</span> && <span class="hljs-variable language_">global</span> !== <span class="hljs-literal">null</span> && <span class="hljs-variable language_">global</span>.<span class="hljs-property">Object</span> === <span class="hljs-title class_">Object</span> && <span class="hljs-variable language_">global</span><br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> freeGlobal<br></code></pre></td></tr></table></figure></blockquote>]]></content>
<summary type="html">typeof 是javascript中的关键字,但如果你认为它只是用来判断类型,那你可就大错特错了</summary>
<category term="javascript" scheme="https://www.yxlazy.xyz/categories/javascript/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
<category term="源码解析" scheme="https://www.yxlazy.xyz/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
<category term="lodash" scheme="https://www.yxlazy.xyz/tags/lodash/"/>
</entry>
<entry>
<title>eslint 搭配 vscode 的简单使用</title>
<link href="https://www.yxlazy.xyz/2021/05/12/eslint-%E6%90%AD%E9%85%8D-vscode-%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/"/>
<id>https://www.yxlazy.xyz/2021/05/12/eslint-%E6%90%AD%E9%85%8D-vscode-%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/</id>
<published>2021-05-12T04:22:13.000Z</published>
<updated>2023-01-12T08:20:42.158Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>刚开始时,由于嫌麻烦,并没有安装<code>eslint</code>,最近在新的项目上使用了<code>eslint</code>再配合<code>vscode</code>的插件,真是爽的不要太爽。因此打算写一篇简单的食用说明来记录食用过程</p><h2 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h2><p>没啥好准备的,作为开发肯定是具备<code>yarn</code>和<code>node</code>的,编辑器使用的是微软的亲儿子<code>vscode</code> </p><p>然后新建一个文件夹<code>eslint-example</code>,<code>cd</code>进入这个文件夹并初始化一个<code>package.json</code></p><p>初始化<code>package.json</code>命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ yarn init -y<br></code></pre></td></tr></table></figure><p>文件结构</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">-</span> eslint-example<br><span class="hljs-bullet">-</span> package.json<br></code></pre></td></tr></table></figure><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>首先安装<code>eslint</code>,并初始化一个配置文件 </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><code class="hljs bash">$ yarn add eslint --dev<br>$ ./node_modules/.bin/eslint --init<br></code></pre></td></tr></table></figure><p>初始化完成后会在项目的根目录下生成一个配置文件<code>.eslintrc.js</code>(你的可能和我的不一样,但前缀都是一样的) </p><blockquote><p>关于配置文件的一些说明,配置文件可以使用<code>.js</code>,<code>.json</code>,<code>.yaml/.yml</code>后缀或者没有后缀的<code>.eslintrc</code>文件,也可以直接在<code>package.json</code>中添加一个<code>eslintConfig</code>属性进行配置。<code>eslint</code>会读取这个文件,优先级为从左到右依次查找文件格式,没有后缀的配置文件声明已废弃,不建议使用</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-variable language_">module</span>.<span class="hljs-property">exports</span> = {<br>...<br> <span class="hljs-comment">//在这里添加自定义规则去覆盖默认规则</span><br> <span class="hljs-string">"rules"</span>: {<br> <span class="hljs-comment">//要求或禁止使用分号代替 ASI</span><br> <span class="hljs-string">"semi"</span>: [<span class="hljs-string">"error"</span>, <span class="hljs-string">"always"</span>],<br> <span class="hljs-comment">//强制使用一致的缩进</span><br> <span class="hljs-string">"indent"</span>: [<span class="hljs-string">"error"</span>, <span class="hljs-number">2</span>],<br> <span class="hljs-comment">//强制使用一致的反勾号、双引号或单引号</span><br> <span class="hljs-string">"quotes"</span>: [<span class="hljs-string">"error"</span>, <span class="hljs-string">"double"</span>],<br> <span class="hljs-comment">//禁用console</span><br> <span class="hljs-string">"no-console"</span>: <span class="hljs-string">"warn"</span> <span class="hljs-comment">// 可以直接写错误类型</span><br> }<br> ...<br>}<br></code></pre></td></tr></table></figure><p><code>eslint</code>有三种错误规则,可以直接写规则类型也可以直接写数字,错误规则:</p><ul><li><code>error</code>对应数字<code>2</code></li><li><code>warn</code>对应数字<code>1</code></li><li><code>off</code>对应数字<code>0</code></li></ul><p>在<code>package.json</code>中添加<code>scripts</code></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs json">...<br><span class="hljs-attr">"script"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"lint"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"eslint --ext .js src"</span><br><span class="hljs-punctuation">}</span><br>...<br></code></pre></td></tr></table></figure><p>运行命令验证</p><h2 id="配合vscode插件食用更舒适"><a href="#配合vscode插件食用更舒适" class="headerlink" title="配合vscode插件食用更舒适"></a>配合vscode插件食用更舒适</h2><p>在<code>vscode</code>的插件栏搜索<code>eslint</code>,安装<code>ESLint</code>插件</p><p>然后在<code>settings.json</code>中添加如下配置,对于更详细的配置请查看插件文档<sup>[2]</sup> </p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs json">...<br><span class="hljs-comment">//为eslint开启自动修复,保存时将触发,但并不能自动修复所有的问题,但已经很爽了</span><br><span class="hljs-attr">"editor.codeActionsOnSave"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"source.fixAll.eslint"</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><br><span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br></code></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>[1] <a href="https://eslint.org/docs/user-guide/">https://eslint.org/docs/user-guide/</a> </p><p>[2] <a href="https://github.com/Microsoft/vscode-eslint#readme">https://github.com/Microsoft/vscode-eslint#readme</a> </p>]]></content>
<summary type="html">ESLint 是在 ECMAScript/JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误...</summary>
<category term="javascript" scheme="https://www.yxlazy.xyz/categories/javascript/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
</entry>
<entry>
<title>使用canal连接kafka</title>
<link href="https://www.yxlazy.xyz/2021/04/20/%E4%BD%BF%E7%94%A8canal%E8%BF%9E%E6%8E%A5kafka/"/>
<id>https://www.yxlazy.xyz/2021/04/20/%E4%BD%BF%E7%94%A8canal%E8%BF%9E%E6%8E%A5kafka/</id>
<published>2021-04-20T06:06:49.000Z</published>
<updated>2023-01-12T08:20:42.166Z</updated>
<content type="html"><![CDATA[<h1 id="使用canal连接kafka"><a href="#使用canal连接kafka" class="headerlink" title="使用canal连接kafka"></a>使用canal连接kafka</h1><p>这篇主要是项目还原,目的是记录构建时遇到的各种奇葩坑,避免下次迷路。废话不多说,直接上手。</p><blockquote><p>默认已安装<code>docker</code>,<code>docker-compose</code>,<code>nodejs</code>,<code>yarn</code>,<code>typescript</code> </p></blockquote><ol><li>首先根据 <a href="https://github.com/wurstmeister/kafka-docker/blob/master/docker-compose.yml">kafka-docker</a> 这个官方的仓库下的<code>docker-compose.yml</code>复制一份到自己的项目中</li></ol><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">'2'</span><br><span class="hljs-attr">services:</span><br> <span class="hljs-attr">zookeeper:</span><br> <span class="hljs-attr">image:</span> <span class="hljs-string">wurstmeister/zookeeper</span><br> <span class="hljs-attr">ports:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">"2181:2181"</span><br> <span class="hljs-attr">kafka:</span><br> <span class="hljs-attr">build:</span> <span class="hljs-string">.</span><br> <span class="hljs-attr">ports:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">"9092"</span><br> <span class="hljs-attr">environment:</span><br> <span class="hljs-comment"># 更改为自己的ip地址,最好是ip地址,我先使用localhost</span><br> <span class="hljs-attr">KAFKA_ADVERTISED_HOST_NAME:</span> <span class="hljs-string">localhost</span><br> <span class="hljs-attr">KAFKA_ZOOKEEPER_CONNECT:</span> <span class="hljs-string">zookeeper:2181</span><br> <span class="hljs-attr">volumes:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">/var/run/docker.sock:/var/run/docker.sock</span><br></code></pre></td></tr></table></figure><p>将<code>kafka</code>下的<code>build</code>项,更改为<code>kafka</code>镜像,可以从<a href="https://hub.docker.com/r/wurstmeister/kafka">dockerhub</a>中查找指定版本的<code>kafka</code>,这里使用<code>wurstmeister/kafka:2.13-2.7.0</code></p><p>在<code>environment</code>下添加配置属性</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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><code class="hljs yaml"><span class="hljs-string">...</span><br> <span class="hljs-attr">kafka:</span><br> <span class="hljs-attr">image:</span> <span class="hljs-string">wurstmeister/kafka:2.13-2.7.0</span><br> <span class="hljs-attr">ports:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">"9092:9092"</span> <span class="hljs-comment">#向外暴露端口</span><br> <span class="hljs-attr">environment:</span><br> <span class="hljs-attr">KAFKA_BROKER_ID:</span> <span class="hljs-number">1</span> <span class="hljs-comment">#新增一个broker id</span><br> <span class="hljs-attr">KAFKA_ADVERTISED_HOST_NAME:</span> <span class="hljs-string">localhost</span><br> <span class="hljs-attr">KAFKA_ZOOKEEPER_CONNECT:</span> <span class="hljs-string">zookeeper:2181</span><br> <span class="hljs-attr">KAFKA_CREATE_TOPICS:</span> <span class="hljs-string">"test:2:1"</span> <span class="hljs-comment">#新增一个topic或多个 </span><br><span class="hljs-string">...</span><br></code></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><code class="hljs bash">$ docker-compose up<br></code></pre></td></tr></table></figure><ol start="2"><li><p>编写<code>Producer</code>和<code>Customer</code> </p><p><code>kafkajs</code>版</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//config.ts //简单的配置</span><br><span class="hljs-keyword">const</span> <span class="hljs-title class_">Config</span> = {<br> <span class="hljs-attr">brokers</span>: [<br> <span class="hljs-string">"localhost:9092"</span> <span class="hljs-comment">//kafka的服务器</span><br> ],<br> <span class="hljs-attr">topic</span>: <span class="hljs-string">'test'</span> <span class="hljs-comment">//与kafka添加的topcs一样</span><br>}<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">Config</span>;<br><br><br><span class="hljs-comment">//kafka.ts //实例化一个kafkajs对象</span><br><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Kafka</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"kafkajs"</span>;<br><span class="hljs-keyword">import</span> <span class="hljs-title class_">Config</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./config"</span>;<br><br><span class="hljs-keyword">const</span> kafka = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Kafka</span>({<br> <span class="hljs-attr">clientId</span>: <span class="hljs-string">'kafkajs'</span>,<br> <span class="hljs-attr">brokers</span>: <span class="hljs-title class_">Config</span>.<span class="hljs-property">brokers</span><br>});<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> kafka;<br><br><span class="hljs-comment">//producer.ts //kafka Producer</span><br><span class="hljs-keyword">import</span> { <span class="hljs-title class_">Message</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"kafkajs"</span>;<br><span class="hljs-keyword">import</span> <span class="hljs-title class_">Config</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./config"</span>;<br><span class="hljs-keyword">import</span> kafka <span class="hljs-keyword">from</span> <span class="hljs-string">"./kafka"</span>;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">producer</span>(<span class="hljs-params">messages: Message[]</span>) {<br> <span class="hljs-keyword">const</span> producer = kafka.<span class="hljs-title function_">producer</span>();<br> <span class="hljs-keyword">await</span> producer.<span class="hljs-title function_">connect</span>();<br> <span class="hljs-keyword">await</span> producer.<span class="hljs-title function_">send</span>({<br> <span class="hljs-attr">topic</span>: <span class="hljs-title class_">Config</span>.<span class="hljs-property">topic</span>,<br> messages<br> });<br> <span class="hljs-keyword">await</span> producer.<span class="hljs-title function_">disconnect</span>()<br>}<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> producer;<br><br><span class="hljs-comment">//consumer.ts //kafka Consumer</span><br><span class="hljs-keyword">import</span> kafka <span class="hljs-keyword">from</span> <span class="hljs-string">"./kafka"</span>;<br><span class="hljs-keyword">import</span> <span class="hljs-title class_">Config</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"./config"</span>;<br><span class="hljs-keyword">import</span> { <span class="hljs-title class_">ConsumerConfig</span>, <span class="hljs-title class_">EachMessagePayload</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"kafkajs"</span>;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">consumer</span>(<span class="hljs-params">config: ConsumerConfig</span>) {<br> <span class="hljs-keyword">const</span> consumer = kafka.<span class="hljs-title function_">consumer</span>(config);<br> <span class="hljs-keyword">await</span> consumer.<span class="hljs-title function_">connect</span>()<br> <span class="hljs-keyword">await</span> consumer.<span class="hljs-title function_">subscribe</span>({<br> <span class="hljs-attr">topic</span>: <span class="hljs-title class_">Config</span>.<span class="hljs-property">topic</span>,<br> <span class="hljs-attr">fromBeginning</span>: <span class="hljs-literal">true</span><br> });<br> <span class="hljs-keyword">await</span> consumer.<span class="hljs-title function_">run</span>({<br> <span class="hljs-attr">eachMessage</span>: <span class="hljs-keyword">async</span> ({topic, partition, message}: <span class="hljs-title class_">EachMessagePayload</span>): <span class="hljs-title class_">Promise</span><<span class="hljs-keyword">void</span>> => {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>({<br> <span class="hljs-attr">value</span>: message.<span class="hljs-property">value</span>.<span class="hljs-title function_">toString</span>(),<br> topic,<br> partition<br> })<br> }<br> })<br>}<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> consumer;<br><br><span class="hljs-comment">//index.ts //函数入口</span><br><span class="hljs-keyword">import</span> producer <span class="hljs-keyword">from</span> <span class="hljs-string">"./producer"</span>;<br><span class="hljs-keyword">import</span> consumer <span class="hljs-keyword">from</span> <span class="hljs-string">'./consumer'</span>;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">start</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">await</span> <span class="hljs-title function_">producer</span>([<br> {<span class="hljs-attr">value</span>: <span class="hljs-string">'Hello'</span>},<br> {<span class="hljs-attr">value</span>: <span class="hljs-string">','</span>},<br> {<span class="hljs-attr">value</span>: <span class="hljs-string">'I\'m'</span>},<br> {<span class="hljs-attr">value</span>: <span class="hljs-string">'kafkajs'</span>}<br> ])<br><br> <span class="hljs-keyword">await</span> <span class="hljs-title function_">consumer</span>({<br> <span class="hljs-attr">groupId</span>: <span class="hljs-string">'consumer-1'</span><br> })<br> <span class="hljs-keyword">await</span> <span class="hljs-title function_">consumer</span>({<br> <span class="hljs-attr">groupId</span>: <span class="hljs-string">'consumer-2'</span><br> })<br>}<br><br><span class="hljs-title function_">start</span>()<br>.<span class="hljs-title function_">catch</span>(<span class="hljs-variable language_">console</span>.<span class="hljs-property">log</span>)<br></code></pre></td></tr></table></figure><p>然后编译文件,并运行,可以看到我们消息从<code>Producer</code>发送到了<code>Consumer</code></p></li><li><p>接入<code>canal</code></p><p>修改<code>docker-compose.yml</code>,添加<code>canal</code>的镜像和相关配置,同时添加一个测试的<code>mysql</code>镜像(注,由于项目需求,我还配置了<code>wordpress</code>镜像)</p><p><a href="https://github.com/alibaba/canal/wiki/AdminGuide">canal配置说明</a> </p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><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></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-string">...</span><br> <span class="hljs-attr">canal:</span><br> <span class="hljs-attr">image:</span> <span class="hljs-string">canal/canal-server:v1.1.4</span><br> <span class="hljs-attr">environment:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.instance.mysql.slaveId=54321</span> <span class="hljs-comment">#slave id 不要与mysql的一样就行</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.instance.master.address=mysql:3306</span> <span class="hljs-comment">#mysql地址</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.instance.dbUsername=kafka</span> <span class="hljs-comment">#mysql 对应的用户名</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.instance.dbPassword=kafka</span> <span class="hljs-comment">#mysql 对应的密码</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.instance.parser.parallel=false</span> <span class="hljs-comment">#由于我用的虚拟机,cpu为1G,所以设为false</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.instance.filter.regex=kafka\.user</span> <span class="hljs-comment">#数据库中要监听的表,详细看官方说明</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.mq.dynamicTopic=.*\..*</span> <span class="hljs-comment">#动态生成topic</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.zkServers=zookeeper:2181</span> <span class="hljs-comment">#链接zookeeper集群的链接信息</span><br> <span class="hljs-comment">#canal.properties 配置</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.serverMode=kafka</span> <span class="hljs-comment">#MQ使用的kafka</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">canal.mq.servers=kafka:9092</span> <span class="hljs-comment">#kafka地址</span><br> <span class="hljs-attr">depends_on:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">zookeeper</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">kafka</span><br><br> <span class="hljs-attr">mysql:</span><br> <span class="hljs-attr">image:</span> <span class="hljs-string">mysql:5.7</span><br> <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span><br> <span class="hljs-attr">volumes:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">./configuration/conf.d/binlog.cnf:/etc/mysql/conf.d/binlog.cnf</span> <span class="hljs-comment">#为了让mysql开启bin_log模式的配置</span><br> <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span><br> <span class="hljs-attr">environment:</span><br> <span class="hljs-attr">MYSQL_ROOT_PASSWORD:</span> <span class="hljs-string">root_password_can_you</span><br> <span class="hljs-attr">MYSQL_DATABASE:</span> <span class="hljs-string">kafkadb</span><br> <span class="hljs-attr">MYSQL_USER:</span> <span class="hljs-string">kafka</span><br> <span class="hljs-attr">MYSQL_PASSWORD:</span> <span class="hljs-string">kafka</span><br> <span class="hljs-attr">ports:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-number">3306</span><span class="hljs-string">:3306</span><br><br></code></pre></td></tr></table></figure><p><code>binlog.cnf</code>配置文件内容,<a href="https://github.com/alibaba/canal/wiki/QuickStart#%E5%87%86%E5%A4%87">canal官方说明</a> </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></pre></td><td class="code"><pre><code class="hljs mysql">[mysqld]<br>log-bin=mysql-bin # 开启 binlog<br>binlog-format=ROW # 选择 ROW 模式<br>server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复<br></code></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><code class="hljs bash">$ docker-compose up<br></code></pre></td></tr></table></figure><p>更改<code>mysql</code>权限 ,使用<code>root</code>登录到<code>mysql</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></pre></td><td class="code"><pre><code class="hljs mysql">CREATE USER kafka IDENTIFIED BY 'kafka'; # 创建与docker-compose.yml中对应的用户和密码<br>GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'kafka'@'%'; #给mysql用户权限<br>-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ; #也可以给所有权限<br>FLUSH PRIVILEGES;<br></code></pre></td></tr></table></figure><p>创建一个数据库<code>kafkadb</code>并添加一个<code>user</code>表</p><p>向<code>user</code>表插入数据</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs mysql">INSERT INTO user ( `id`, `username`) VALUES ( 1, 'yan');<br></code></pre></td></tr></table></figure><p>好像没有数据过来(至少我的是这样)</p></li><li><p>排查问题</p><p>首先查看是否镜像运行正常</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ docker ps <br></code></pre></td></tr></table></figure><p>发现没有问题,只有依次排查每个镜像日志,先从<code>canal</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><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash">$ docker <span class="hljs-built_in">exec</span> -it <canal 镜像> bash<br><span class="hljs-comment">#然后进入canal-server文件夹</span><br>$ <span class="hljs-built_in">cd</span> canal-server<br>$ <span class="hljs-built_in">cat</span> logs/example/example.log<br><span class="hljs-comment">#发现出错了,以下为片段</span><br><span class="hljs-comment"># Caused by: java.util.concurrent.ExecutionException: org.apache.kafka.common.errors.TimeoutException: Failed to update metadata after 60000 ms.</span><br></code></pre></td></tr></table></figure><p>百度后,发现和<a href="https://blog.csdn.net/maoyuanming0806/article/details/80553632">这个问题很像</a>,那应该就是我们前面说的<code>kafka</code>的<code>ip</code>设置成<code>localhost</code>导致的,尝试更改一下,问题解决</p><p>再插入数据,可以看到数据被接收到了</p></li></ol><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>其实在部署之间,遇到了很多问题,由于这次是问题重现,有些问题并没有再出现</p><p>例如有自己写的<code>Producer</code>程序推送消息时,报错<code>There is no leader for this topic-partition as we are in the middle of a leadership election</code> 这是由于,没有设置<code>KAFKA_BROKER_ID</code>导致每次构建项目,都重新生成了<code>brokder id</code>,可以在构建项目时在其后添加<code>--no-recreate</code> ,<a href="https://github.com/wurstmeister/kafka-docker/issues/516">可以再这里找到</a> 。记得使用<code>docker-compose rm -vfs</code>删除后再构建项目</p><p>也有<code>zookeeper</code>报错<code>Zookeeper Report Error:KeeperErrorCode = NoNode</code>,<a href="https://github.com/wurstmeister/kafka-docker/issues/427">可以再这里找到</a> </p><p>等等</p>]]></content>
<summary type="html"><h1 id="使用canal连接kafka"><a href="#使用canal连接kafka" class="headerlink" title="使用canal连接kafka"></a>使用canal连接kafka</h1><p>这篇主要是项目还原,目的是记录构建时遇到的各</summary>
<category term="other" scheme="https://www.yxlazy.xyz/categories/other/"/>
<category term="kafka" scheme="https://www.yxlazy.xyz/tags/kafka/"/>
</entry>
<entry>
<title>定型数组(typed array)</title>
<link href="https://www.yxlazy.xyz/2021/04/09/%E5%AE%9A%E5%9E%8B%E6%95%B0%E7%BB%84-typed-array/"/>
<id>https://www.yxlazy.xyz/2021/04/09/%E5%AE%9A%E5%9E%8B%E6%95%B0%E7%BB%84-typed-array/</id>
<published>2021-04-09T06:12:33.000Z</published>
<updated>2023-01-12T08:20:42.174Z</updated>
<content type="html"><![CDATA[<h1 id="定型数组(typed-array"><a href="#定型数组(typed-array" class="headerlink" title="定型数组(typed array)"></a>定型数组(typed array)</h1><p>定型数组<code>(typed array)</code>是<code>ECMAScript</code>新增的结构,目的是提升向原生库传输数据的效率。实际上,<code>JavaScript</code>并没有<code>"TypedArray"</code>类型,它所指的其实是一种特殊的包含数值类型的数组</p><h2 id="ArrayBuffer"><a href="#ArrayBuffer" class="headerlink" title="ArrayBuffer"></a>ArrayBuffer</h2><p><code>ArrayBuffer</code>是所有定型数组及视图引用的<strong>基本单位</strong>。</p><blockquote><p><code>SharedArrayBuffer</code>是<code>ArrayBuffer</code>的一个变体,可以无须复制就在执行上下文间传递它</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayBuffer</span>(<span class="hljs-number">2</span>) <span class="hljs-comment">//在内存中分配2个字节</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(buf)<span class="hljs-comment">//ArrayBuffer(2) {}</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(buf.<span class="hljs-property">byteLength</span>) <span class="hljs-comment">//2 //返回分配的字节长度</span><br></code></pre></td></tr></table></figure><p><code>ArrayBuffer</code>一经创建就不能再调整大小</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayBuffer</span>(<span class="hljs-number">16</span>)<br><span class="hljs-keyword">const</span> buf2 = buf.<span class="hljs-title function_">slice</span>(<span class="hljs-number">4</span>, <span class="hljs-number">12</span>) <span class="hljs-comment">//但可以使用slice()复制部分到新的实例中</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(buf2.<span class="hljs-property">byteLength</span>) <span class="hljs-comment">//8</span><br></code></pre></td></tr></table></figure><p><code>ArrayBuffer</code>分配的堆内存可以被当成垃圾回收,不用手动释放(相比于<code>C/C++</code>的<code>malloc()</code>)</p><h2 id="DataView"><a href="#DataView" class="headerlink" title="DataView"></a>DataView</h2><p><code>DataView</code>用于读写<code>ArrayBuffer</code>的视图。专为文件<code>I/O</code>和网络<code>I/O</code>设计,其<code>API</code>支持对缓冲数据的高度控制,但较其他类型的视图,性能差一些。</p><p>必须对已有的<code>ArrayBuffer</code>进行读/写才能创建<code>DataView</code>实例</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayBuffer</span>(<span class="hljs-number">16</span>)<br><span class="hljs-keyword">const</span> dv = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataView</span>(buf) <span class="hljs-comment">//默认使用整个ArrayBuffer</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv) <span class="hljs-comment">//DataView(16) {} </span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv.<span class="hljs-property">byteLength</span>) <span class="hljs-comment">//16</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv.<span class="hljs-property">byteOffset</span>) <span class="hljs-comment">//0</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv.<span class="hljs-property">buffer</span> === buf) <span class="hljs-comment">//true</span><br><br><span class="hljs-comment">//使用部分ArrayBuffer</span><br><span class="hljs-keyword">const</span> dv2 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataView</span>(buf, <span class="hljs-comment">/*offset*/</span><span class="hljs-number">2</span>, <span class="hljs-comment">/*length*/</span><span class="hljs-number">4</span>)<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv2) <span class="hljs-comment">//DataView(4) {}</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv2.<span class="hljs-property">byteLength</span>) <span class="hljs-comment">//4</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv2.<span class="hljs-property">byteOffset</span>) <span class="hljs-comment">//2</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv2.<span class="hljs-property">buffer</span> === buf) <span class="hljs-comment">//true</span><br></code></pre></td></tr></table></figure><p>读取缓冲,需要几个<code>ElementType</code>组件,类似于<code>C</code>中的类型</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayBuffer</span>(<span class="hljs-number">16</span>)<br><span class="hljs-keyword">const</span> dv = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataView</span>(buf, <span class="hljs-number">0</span>, <span class="hljs-number">8</span>)<br><br><span class="hljs-comment">//说明DataView的实例默认值初始值为0</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv.<span class="hljs-title function_">getInt8</span>(<span class="hljs-number">0</span>)) <span class="hljs-comment">//0 //以8位有符号整数进行读取,读取第一个字节</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv.<span class="hljs-title function_">getInt8</span>(<span class="hljs-number">1</span>)) <span class="hljs-comment">//0</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv.<span class="hljs-title function_">getInt16</span>(<span class="hljs-number">0</span>)) <span class="hljs-comment">//0 //以16位有符号整数进行读取,读取两个字节</span><br><span class="hljs-comment">//写入</span><br>dv.<span class="hljs-title function_">setInt8</span>(<span class="hljs-number">1</span>, <span class="hljs-number">255</span>) <span class="hljs-comment">//给第二个字节写入255,二进制每一位都是1</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv.<span class="hljs-title function_">getInt8</span>(<span class="hljs-number">1</span>)) <span class="hljs-comment">//-1 //由于是有符号的读,会被当做二补数(即反码)处理,字节开头的1被当作符号位</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(dv.<span class="hljs-title function_">getUint8</span>(<span class="hljs-number">1</span>)) <span class="hljs-comment">//255 //无符号读取</span><br></code></pre></td></tr></table></figure><p>除了上述的类型(<code>Int8, Uint8, Int16</code>)还有<code>Uint16,Int32, Uint32, Float32, Float64 </code>。默认的字节序为大端字节序。传递第二个参数可以决定字节序,为<code>true</code>时,则启用小端字节序</p><blockquote><p><code>DataView</code> 只支持两种约定:大端字节序和小端字节序。大端字节序也称为“网络字节序”,意思是最高有效位保存在第一个字节,而最低有效位保存在最后一个字节。小端字节序正好相反,即最低有效位保存在第一个字节,最高有效位保存在最后一个字节。</p></blockquote><p><code>DataView</code>的读、写操作都必须满足充足的缓冲区,如果越界会抛出<code>RangError</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayBuffer</span>(<span class="hljs-number">2</span>)<br><span class="hljs-keyword">const</span> dv = <span class="hljs-keyword">new</span> <span class="hljs-title class_">DataView</span>(buf) <span class="hljs-comment">//这里为2个字节</span><br><span class="hljs-comment">//尝试越界读取4个字节</span><br>dv.<span class="hljs-title function_">getInt32</span>(<span class="hljs-number">0</span>) <span class="hljs-comment">//错误 RangeError: Offset is outside the bounds of the DataView</span><br></code></pre></td></tr></table></figure><h2 id="定型数组"><a href="#定型数组" class="headerlink" title="定型数组"></a>定型数组</h2><p>定型数组是另一种形式的<code>ArrayBffer</code>视图。设计定型数组的目的就是提高与 <code>WebGL</code> 等原生库交换二进制数据的效率。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//创建一个12字节的缓冲</span><br><span class="hljs-keyword">const</span> buf = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayBuffer</span>(<span class="hljs-number">12</span>)<br><span class="hljs-comment">//创建一个引用buf缓冲的Int32Array</span><br><span class="hljs-keyword">const</span> ints = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Int32Array</span>(buf)<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints) <span class="hljs-comment">//Int32Array(3) [0, 0, 0]</span><br><span class="hljs-comment">//length属性,打印长度</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints.<span class="hljs-property">length</span>) <span class="hljs-comment">//3</span><br><span class="hljs-comment">//与DataView一样,也有一个指向关联缓冲的引用</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints.<span class="hljs-property">buffer</span>) <span class="hljs-comment">//ArrayBuffer(12) {}</span><br><br><span class="hljs-comment">//创建一个指定长度的Int32Array</span><br><span class="hljs-keyword">const</span> ints1 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Int32Array</span>(<span class="hljs-number">6</span>)<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints1.<span class="hljs-property">length</span>) <span class="hljs-comment">//6</span><br><br><span class="hljs-comment">//传入数组初始化Int32Array</span><br><span class="hljs-keyword">const</span> ints2 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Int32Array</span>([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>])<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints2.<span class="hljs-property">length</span>) <span class="hljs-comment">//4</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints2[<span class="hljs-number">1</span>]) <span class="hljs-comment">//2</span><br><br><span class="hljs-comment">//通过<ElementType>.from和<ElementType>.to初始化Int32Array</span><br><span class="hljs-keyword">const</span> ints3 = <span class="hljs-title class_">Int32Array</span>.<span class="hljs-title function_">from</span>(ints2)<br><span class="hljs-keyword">const</span> ints4 = <span class="hljs-title class_">Int32Array</span>.<span class="hljs-title function_">of</span>(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">30</span>, <span class="hljs-number">40</span>)<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints3.<span class="hljs-property">length</span>) <span class="hljs-comment">//4</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints4.<span class="hljs-property">length</span>) <span class="hljs-comment">//4</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints3[<span class="hljs-number">1</span>]) <span class="hljs-comment">//20</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints4[<span class="hljs-number">1</span>]) <span class="hljs-comment">//20</span><br><br><span class="hljs-comment">//BYTES_PER_ELEMENT属性返回类型数组中每个元素的大小</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title class_">Int32Array</span>.<span class="hljs-property">BYTES_PER_ELEMENT</span>) <span class="hljs-comment">//4</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title class_">Float64Array</span>.<span class="hljs-property">BYTES_PER_ELEMENT</span>) <span class="hljs-comment">//8</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(ints4.<span class="hljs-property">BYTES_PER_ELEMENT</span>) <span class="hljs-comment">//4</span><br></code></pre></td></tr></table></figure><p>定型数组支持数组的方法和属性,包括<code>Symbol.iterator</code>符号属性,但是没有<code>concat(), poop(), push(), shift(), unshift(), splice()</code>。定型数组提供了两个方法可以实现复制数据:<code>set()</code>和<code>subarray()</code></p><p><code>set()</code>从提供的数组或定型数组中把值复制到当前的定型数组中指定索引位置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> container = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Int8Array</span>(<span class="hljs-number">8</span>)<br><span class="hljs-comment">//复制到从偏移量为2的位置开始的地方,默认偏移量为0</span><br>container.<span class="hljs-title function_">set</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Int8Array</span>([<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>]), <span class="hljs-number">2</span>)<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(container) <span class="hljs-comment">//[0, 0, 2, 3, 4, 5, 0, 0]</span><br><br><span class="hljs-comment">//溢出会抛出错误</span><br>container.<span class="hljs-title function_">set</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Int8Array</span>([<span class="hljs-number">3</span>, <span class="hljs-number">4</span>,<span class="hljs-number">5</span>]), <span class="hljs-number">7</span>) <span class="hljs-comment">//错误 RangeError: offset is out of bounds</span><br></code></pre></td></tr></table></figure><p><code>subarray()</code>会基于原始定型数组返回复制的值组成新的定型数组</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> source = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Int8Array</span>([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>])<br><span class="hljs-comment">//复制索引2到4,左闭右开</span><br><span class="hljs-keyword">const</span> newSource = source.<span class="hljs-title function_">subarray</span>(<span class="hljs-number">2</span>, <span class="hljs-number">4</span>)<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(newSource) <span class="hljs-comment">// [3, 4]</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(newSource) <span class="hljs-comment">//Int8Array(2) [3, 4]</span><br></code></pre></td></tr></table></figure><p>除了<code>8</code>种元素类型,还有一种”夹板“数组类型:<code>Uint8ClampedArray</code>,用于解决溢出问题。该类型不允许任何方向溢出,超出最大值<code>255</code>的值会被向下舍入为<code>255</code>,而小于最小值<code>0</code>的值会被向上舍入为<code>0</code></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> clampedInts = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Uint8ClampedArray</span>([-<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">255</span>, <span class="hljs-number">256</span>])<br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(clampedInts) <span class="hljs-comment">//[0, 0, 255, 255]</span><br></code></pre></td></tr></table></figure><blockquote><p>除非做的和<code>canvas</code>相关开发,否则不要使用它</p></blockquote>]]></content>
<summary type="html"><h1 id="定型数组(typed-array"><a href="#定型数组(typed-array" class="headerlink" title="定型数组(typed array)"></a>定型数组(typed array)</h1><p>定型数组<code>(t</summary>
<category term="javascript" scheme="https://www.yxlazy.xyz/categories/javascript/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
</entry>
<entry>
<title>执行上下文,作用域和垃圾清理</title>
<link href="https://www.yxlazy.xyz/2021/03/27/%E6%89%A7%E8%A1%8C%E4%B8%8A%E4%B8%8B%E6%96%87%EF%BC%8C%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%9E%83%E5%9C%BE%E6%B8%85%E7%90%86/"/>
<id>https://www.yxlazy.xyz/2021/03/27/%E6%89%A7%E8%A1%8C%E4%B8%8A%E4%B8%8B%E6%96%87%EF%BC%8C%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%9E%83%E5%9C%BE%E6%B8%85%E7%90%86/</id>
<published>2021-03-27T06:04:20.000Z</published>
<updated>2023-01-12T08:20:42.174Z</updated>
<content type="html"><![CDATA[<h1 id="执行上下文,作用域和垃圾回收"><a href="#执行上下文,作用域和垃圾回收" class="headerlink" title="执行上下文,作用域和垃圾回收"></a>执行上下文,作用域和垃圾回收</h1><p>执行上下文是当前<code>JavaScript</code>代码被解析和执行时所在环境的抽象概念。变量或函数的上下文决定了它们可以访问哪些数据。</p><h2 id="执行上下文的类型"><a href="#执行上下文的类型" class="headerlink" title="执行上下文的类型"></a>执行上下文的类型</h2><p>执行上下文包含全局执行上下文、函数执行上下文和<code>eval</code>执行上下文(不建议使用)</p><p>全局执行上下文:是最外层的上下文,在浏览器环境中就是全局对象<code>window</code>,<code>this</code>指向这个全局对象。全局执行上下文仅此一个</p><p>函数执行上下文:当函数被调用时,会创建一个函数执行上下文并推到一个上下文栈,等到函数执行完,上下文栈会弹出该函数执行上下文。可以存在无数个,且每次调用创建的是一个新的执行上下文</p><p><code>eval</code>函数执行上下文:指的是运行在<code>eval</code>函数中的代码</p><blockquote><ul><li><p>所有通过<code>var</code>定义的全局变量和函数都会成为<code>window</code>对象的属性和方法。使用<code>let</code>和<code>const</code>的顶级声明不会定义在全局上下文中</p></li><li><p>上下文在其所有代码都执行完毕后会被销毁</p></li></ul></blockquote><p>执行栈也叫调用栈,具有<code>LIFO</code>(后进先出)结构,用于存储在代码执行期间创建的执行上下文</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">let</span> scope = <span class="hljs-string">'local scope'</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">test1</span>(<span class="hljs-params"></span>) {<br><span class="hljs-keyword">let</span> scope = <span class="hljs-string">'local scope'</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">fn</span>(<span class="hljs-params"></span>){<br><span class="hljs-keyword">return</span> scope<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-title function_">fn</span>()<br>}<br><span class="hljs-title function_">test1</span>() <span class="hljs-comment">//全局执行上下文入栈 => test1函数执行上下文入栈 => fn函数执行上下文入栈 => 执行完fn函数,fn函数执行上下文出栈 => test1函数执行上下文出栈 => 全局执行上下文出栈</span><br><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">test2</span>(<span class="hljs-params"></span>) {<br><span class="hljs-keyword">let</span> scope = <span class="hljs-string">'local scope'</span><br><span class="hljs-keyword">function</span> <span class="hljs-title function_">fn</span>(<span class="hljs-params"></span>) {<br><span class="hljs-keyword">return</span> scope<br>}<br><span class="hljs-keyword">return</span> fn<br>}<br><span class="hljs-title function_">test2</span>()() <span class="hljs-comment">//全局执行上下文入栈 => test2函数执行上下文入栈 => test2函数执行上下文出栈 => fn函数执行上下文入栈 => fn函数执行上下文出栈 => 全局执行上下文出栈</span><br><br><span class="hljs-comment">//执行结果都一样,但是执行上下文栈变化不一样</span><br></code></pre></td></tr></table></figure><h2 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h2><p>当执行上下文中的代码在执行的时候,会创建变量对象的一个作用域链。这个作用域链决定了各级上下文所能访问到的变量和函数顺序。处在作用域顶端的执行上下文,叫做活动对象。活动对象最初只用<code>arguments</code>。作用域链的中的下一个变量对象来自于包含上下文,再下一个来自于在下一个包含上下文。全局执行上下文的变量对象始终是作用域链的最后一个变量对象。</p><h2 id="垃圾回收"><a href="#垃圾回收" class="headerlink" title="垃圾回收"></a>垃圾回收</h2><p>垃圾回收有两种策略,标记清除法和引用计数法。现阶段浏览器使用的多为标记清楚法。<br>引用计数无法清除循环引用。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">problem</span>(<span class="hljs-params"></span>) {<br><span class="hljs-keyword">let</span> objectA = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Object</span>()<br><span class="hljs-keyword">let</span> objectB = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Object</span>()<br><br>objectA.<span class="hljs-property">someOtherObject</span> = objectB<br>objectB.<span class="hljs-property">anotherObject</span> = objectA<br>}<br><span class="hljs-comment">//objectA和objectB通过各自的属性相互引用,意味着引用数始终都为2</span><br></code></pre></td></tr></table></figure><h2 id="内存泄漏"><a href="#内存泄漏" class="headerlink" title="内存泄漏"></a>内存泄漏</h2><ul><li>缺少声明关键字导致的内存泄漏</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">function</span> <span class="hljs-title function_">test</span>(<span class="hljs-params"></span>) {<br> name = <span class="hljs-string">'yxlazy'</span><br>}<br></code></pre></td></tr></table></figure><p>当调用<code>test()</code>时,会在全局对象<code>window</code>上添加<code>name</code>属性导致内存泄漏,只要<code>window</code>不被清理就不会消失</p><ul><li>定时器导致的内存泄漏</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">let</span> name = <span class="hljs-string">'yxlazy'</span><br><span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(name)<br>}, <span class="hljs-number">1000</span>)<br></code></pre></td></tr></table></figure><p>定时器的回调通过闭包引用了外部变量。只要定时器一直运行,回调函数中引用的<code>name</code>就会一直占用内存</p><ul><li>使用<code>JavaScript</code>闭包造成的内存泄漏</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">let</span> outer = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">let</span> name = <span class="hljs-string">'yxlazy'</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br> <span class="hljs-keyword">return</span> name<br> }<br>}<br></code></pre></td></tr></table></figure><p>调用<code>outer()</code>会导致分配给<code>name</code>的内存被泄漏</p>]]></content>
<summary type="html"><h1 id="执行上下文,作用域和垃圾回收"><a href="#执行上下文,作用域和垃圾回收" class="headerlink" title="执行上下文,作用域和垃圾回收"></a>执行上下文,作用域和垃圾回收</h1><p>执行上下文是当前<code>JavaScri</summary>
<category term="javascript" scheme="https://www.yxlazy.xyz/categories/javascript/"/>
<category term="javascript" scheme="https://www.yxlazy.xyz/tags/javascript/"/>
</entry>
</feed>