-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathatom.xml
470 lines (278 loc) · 282 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>Moorez</title>
<link href="/atom.xml" rel="self"/>
<link href="http://www.shenzekun.cn/"/>
<updated>2020-04-17T17:05:22.607Z</updated>
<id>http://www.shenzekun.cn/</id>
<author>
<name>shenzekun</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>React 16.8.6 版本存在内存泄露</title>
<link href="http://www.shenzekun.cn/React-16-8-6-%E7%89%88%E6%9C%AC%E5%AD%98%E5%9C%A8%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2.html"/>
<id>http://www.shenzekun.cn/React-16-8-6-版本存在内存泄露.html</id>
<published>2020-04-17T17:01:26.000Z</published>
<updated>2020-04-17T17:05:22.607Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>发现这个React 内存泄露问题是某一天的晚上一直开着直播页,直播页用的 react 版本是 16.8.6,到了早上跳到这个页面的时候,控制台有点卡,怀疑是有内存泄露,于是就开始分析这个直播页面。</p></blockquote><a id="more"></a><h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h3><p>打开控制台 <code>performance</code> 面板点击开始录制,如下:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-fbb208fd19c5c448?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>从上图可以发现在这时间内, nodes 节点一直在增长,很有可能发生了内存泄露。</p><p>我们来到 <code>memory</code> 面板分析内存变化:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-c9d26f57023c0ef2?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><blockquote><p>注:上图的蓝色线条表示在时间轴的最后该对象依旧存在,灰色线条则说明对象在时间轴内被分配,但是已经被gc(垃圾回收)了。</p></blockquote><p>上图都是点击 gc 再进行记录的,但是上图还有很多蓝色线条,而且内存一直往上涨,很明显的内存泄露问题,那会是什么导致内存泄露的呢?</p><p>很快发现这里有个 bi 的东西居然占了 31%的大小:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-a7c964ac04ef30bf?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image"></p><p>这个 bi 是用来干嘛的?展开 bi ,鼠标悬浮在 bi 某处:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-f0d68a7fe4d37614?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>发现这个节点是直播间里的进房消息,里面是 react 存的 <code>FiberNode</code> 节点,观察了一下里面这些 bi 基本上都是消息元素。</p><p>选择 <code>summary</code>,选出<code>Detached</code> (分离)的元素:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-195ccdce44c67697?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p><code>Detached HTMLDivElement</code> 居然有41429个,展开,鼠标悬浮:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-acdf0573456b793d?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>还是消息信息,说明是这个导致 bi 增加的。我们继续展开:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-f35f034aa9448df6?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>从上图看 react 会保留消息的上下兄弟节点的引用,而且保留的引用层级有点深,各个节点嵌套依赖,导致一直存在内存里:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-f49a3e0bdf51eebf?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>仔细查下了项目消息相关代码,发现并不会存在有内存泄露的操作。这里简单说下渲染消息的逻辑,消息有进房消息,聊天消息,礼物消息等等,消息展示会根据 messages 数组里面的类型去渲染不同的消息。messages 数组不会无限增长,控制在 100 个,超过就删掉第一个元素,保证维持100个元素渲染消息,但是从上图来看,这些分离的元素并没有被 react 完全删除,还保存在内存里,查了下 React 的 Issue,并查了相关文章,发现有不少人遇到这个问题:</p><ul><li><a href="https://github.com/facebook/react/issues/16138" target="_blank" rel="external">https://github.com/facebook/react/issues/16138</a></li><li><a href="https://github.com/facebook/react/issues/14732" target="_blank" rel="external">https://github.com/facebook/react/issues/14732</a></li><li><a href="https://github.com/facebook/react/issues/18116" target="_blank" rel="external">https://github.com/facebook/react/issues/18116</a></li><li><a href="https://blog.discordapp.com/discord-react-memory-leak-565c89763e8" target="_blank" rel="external">Investigating Discord’s React Memory Leak - Discord Blog</a></li></ul><p>React 核心成员 Dan 给出的解决办法是升级到 16.9.0。</p><p>这里将项目 React 和 React-dom 版本升级16.9.0,<br>发现 FiberNode 节点还是会一直增加。。。</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-d6eea6ef25193edf?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-04531de57817c1b7?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>继续查看Issue 发现,React 版本 <code>0.0.0-241c4467e</code> 修复了这个问题。<br><a href="https://github.com/facebook/react/issues/18066" target="_blank" rel="external">Bug: Detached DOM node memory leak · Issue #18066 · facebook/react · GitHub</a></p><p>这个版本的 mr 已经合并在 React master 上<br><a href="https://github.com/facebook/react/pull/17666" target="_blank" rel="external">Null stateNode after unmount by bvaughn · Pull Request #17666 · facebook/react · GitHub</a></p><p>这个 mr 是 2019 年 12 月 20 号合并的,2019 年 12 月 20 号之后的版本是 16.13.0,这里将项目直接升级到 16.13.1,然后查看 node 节点:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-a0058e635a81f86e?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>发现 node 节点可以降下来了,查看 <code>memory</code> 面板:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-fd4ccff974f0da7b?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>对比<code>Snapshot 5</code>,分离的元素没有新增,说明这个版本基本修复了这个问题。</p><h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><p>React <strong>16.8.6</strong> (<strong>16.2.5到16.12.0</strong> 可能会有,这些版本没有验证,但是 issue 里面有人遇到)的版本会存在内存泄露问题,建议升级 React 到 <strong>16.13.0</strong> 以上。</p>]]></content>
<summary type="html">
<hr><blockquote><p>发现这个React 内存泄露问题是某一天的晚上一直开着直播页,直播页用的 react 版本是 16.8.6,到了早上跳到这个页面的时候,控制台有点卡,怀疑是有内存泄露,于是就开始分析这个直播页面。</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="react" scheme="http://www.shenzekun.cn/tags/react/"/>
</entry>
<entry>
<title>IVWEB 玩转 WASM 系列-WEBGL YUV渲染图像实践</title>
<link href="http://www.shenzekun.cn/IVWEB-%E7%8E%A9%E8%BD%AC-WASM-%E7%B3%BB%E5%88%97-WEBGL-YUV%E6%B8%B2%E6%9F%93%E5%9B%BE%E5%83%8F%E5%AE%9E%E8%B7%B5.html"/>
<id>http://www.shenzekun.cn/IVWEB-玩转-WASM-系列-WEBGL-YUV渲染图像实践.html</id>
<published>2019-12-14T05:20:14.000Z</published>
<updated>2019-12-14T05:21:43.417Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>最近团队在用 WASM + FFmpeg 打造一个 WEB 播放器。我们是通过写 C 语言用 FFmpeg 解码视频,通过编译 C 语言转 WASM 运行在浏览器上与 JavaScript 进行通信。默认 FFmpeg 去解码出来的数据是 yuv,而 canvas 只支持渲染 rgb,那么此时我们有两种方法处理这个yuv,第一个使用 FFmpeg 暴露的方法将 yuv 直接转成 rgb 然后给 canvas 进行渲染,第二个使用 webgl 将 yuv 转 rgb ,在 canvas 上渲染。第一个好处是写法很简单,只需 FFmpeg 暴露的方法将 yuv 直接转成 rgb ,缺点呢就是会耗费一定的cpu,第二个好处是会利用 gpu 进行加速,缺点是写法比较繁琐,而且需要熟悉 WEBGL 。考虑到为了减少 cpu 的占用,利用 gpu 进行并行加速,我们采用了第二种方法。</p></blockquote><a id="more"></a><blockquote><p>在讲 YUV 之前,我们先来看下 YUV 是怎么获取到的:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-cb6af60b3e8c735a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="实现播放器必定要经过的步骤"><br>由于我们是写播放器,实现一个播放器的步骤必定会经过以下这几个步骤:</p><ol><li>将视频的文件比如 mp4,avi,flv等等,mp4,avi,flv 相当于是一个容器,里面包含一些信息,比如压缩的视频,压缩的音频等等, 进行解复用,从容器里面提取出压缩的视频以及音频,压缩的视频一般是 H265、H264 格式或者其他格式,压缩的音频一般是 aac或者 mp3。</li><li>分别在压缩的视频和压缩的音频进行解码,得到原始的视频和音频,原始的音频数据一般是pcm ,而原始的视频数据一般是 yuv 或者 rgb。</li><li>然后进行音视频的同步。<br>可以看到解码压缩的视频数据之后,一般就会得到 yuv。</li></ol></blockquote><h2 id="YUV"><a href="#YUV" class="headerlink" title="YUV"></a>YUV</h2><h4 id="YUV-是什么"><a href="#YUV-是什么" class="headerlink" title="YUV 是什么"></a>YUV 是什么</h4><p>对于前端开发者来说,YUV 其实有点陌生,对于搞过音视频开发的一般会接触到这个,简单来说,YUV 和我们熟悉的 RGB 差不多,都是颜色编码方式,只不过它们的三个字母代表的意义与 RGB 不同,YUV 的 “Y” 表示明亮度(Luminance或Luma),也就是灰度值;而 ”U” 和 ”V” 表示的则是色度(Chrominance或Chroma),描述影像色彩及饱和度,用于指定像素的颜色。</p><p>为了让大家对 YUV 有更加直观的感受,我们来看下,Y,U,V 单独显示分别是什么样子,这里使用了 FFmpeg 命令将一张火影忍者的宇智波鼬图片转成YUV420P:<br></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">ffmpeg -<span class="selector-tag">i</span> frame<span class="selector-class">.jpg</span> -s <span class="number">352</span>x288 -pix_fmt yuv420p test.yuv</div></pre></td></tr></table></figure><p></p><p>在 <code>GLYUVPlay</code>软件上打开 <code>test.yuv</code>,显示原图:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-4f0efd72679c542f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="原图"><br>Y分量单独显示:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-f82d8679cb4c6914.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Y"><br>U分量单独显示:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-bf25f3fbb722a897.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="U"><br>V 分量单独显示:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-fcdd0b77fdda4dc6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="V"><br>由上面可以发现,Y 单独显示的时候是可以显示完整的图像的,只不过图片是灰色的。而U,V则代表的是色度,一个偏蓝,一个偏红。</p><h4 id="使用YUV-的好处"><a href="#使用YUV-的好处" class="headerlink" title="使用YUV 的好处"></a>使用YUV 的好处</h4><ol><li>由刚才看到的那样,Y 单独显示是黑白图像,因此YUV格式由彩色转黑白很简单,可以兼容老式黑白电视,这一特性用在于电视信号上。</li><li>YUV的数据尺寸一般都比RGB格式小,可以节约传输的带宽。(但如果用YUV444的话,和RGB24一样都是24位)</li></ol><h4 id="YUV-采样"><a href="#YUV-采样" class="headerlink" title="YUV 采样"></a>YUV 采样</h4><p>常见的YUV的采样有YUV444,YUV422,YUV420:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-e53a07e1488c3c24.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><blockquote><p>注:黑点表示采样该像素点的Y分量,空心圆圈表示采用该像素点的UV分量。</p><ol><li>YUV 4:4:4采样,每一个Y对应一组UV分量。</li><li>YUV 4:2:2采样,每两个Y共用一组UV分量。</li><li>YUV 4:2:0采样,每四个Y共用一组UV分量。</li></ol></blockquote><h4 id="YUV-存储方式"><a href="#YUV-存储方式" class="headerlink" title="YUV 存储方式"></a>YUV 存储方式</h4><p>YUV的存储格式有两类:packed(打包)和 planar(平面):</p><ul><li>packed 的YUV格式,每个像素点的Y,U,V是连续交错存储的。</li><li>planar 的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。</li></ul><p>举个例子,对于 planar 模式,YUV 可以这么存 YYYYUUVV,对于 packed 模式,YUV 可以这么存YUYVYUYV。</p><p>YUV 格式一般有多种,YUV420SP、YUV420P、YUV422P,YUV422SP等,我们来看下比较常见的格式:</p><ul><li><p>YUV420P(每四个 Y 会共用一组 UV 分量):<br><img src="https://upload-images.jianshu.io/upload_images/5308475-5ce99a3cc1a175fb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p></li><li><p>YUV420SP(packed,每四个 Y 会共用一组 UV 分量,和YUV420P不同的是,YUV420SP存储的时候 U,V 是交错存储):<br><img src="https://upload-images.jianshu.io/upload_images/5308475-1a79859ee92b9627.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p></li><li><p>YUV422P(planar,每两个 Y 共用一组 UV 分量,所以 U和 V 会比 YUV420P U 和 V 各多加一行):<br><img src="https://upload-images.jianshu.io/upload_images/5308475-dde49928922dbfca.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p></li></ul><ul><li>YUV422SP(packed,每两个 Y 共用一组 UV 分量):<br><img src="https://upload-images.jianshu.io/upload_images/5308475-6518639277fbe042.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></li></ul><p>其中YUV420P和YUV420SP根据U、V的顺序,又可分出2种格式:</p><ul><li><p><code>YUV420P</code>:U前V后即<code>YUV420P</code>,也叫<code>I420</code>,V前U后,叫<code>YV12</code>。</p></li><li><p><code>YUV420SP</code>:U前V后叫<code>NV12</code>,V前U后叫<code>NV21</code>。</p></li></ul><p>数据排列如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">I420: YYYYYYYY UU VV =>YUV420P</div><div class="line"></div><div class="line">YV12: YYYYYYYY VV UU =>YUV420P</div><div class="line"></div><div class="line">NV12: YYYYYYYY UV UV =>YUV420SP</div><div class="line"></div><div class="line">NV21: YYYYYYYY VU VU =>YUV420SP</div></pre></td></tr></table></figure><p>至于为啥会有这么多格式,经过大量搜索发现原因是为了适配不同的电视广播制式和设备系统,比如 ios 下只有这一种模式<code>NV12</code>,安卓的模式是 <code>NV21</code>,比如 <code>YUV411</code>、<code>YUV420</code>格式多见于数码摄像机数据中,前者用于<code>NTSC</code>制,后者用于 <code>PAL</code>制。至于电视广播制式的介绍我们可以看下这篇文章<a href="http://www.microjie.com/professional-knowledge/82-standards-parterns/52-ntsc-pal-secam?showall=1" target="_blank" rel="external">【标准】NTSC、PAL、SECAM三大制式简介</a></p><h4 id="YUV-计算方法"><a href="#YUV-计算方法" class="headerlink" title="YUV 计算方法"></a>YUV 计算方法</h4><p>以YUV420P存储一张1080 x 1280图片为例子,其存储大小为 <code>((1080 x 1280 x 3) >> 1)</code> 个字节,这个是怎么算出来的?我们来看下面这张图:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-c942a3c1f34ab2c1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>以 Y420P 存储那么 Y 占的大小为 <code>W x H = 1080x1280</code>,U 为<code>(W/2) * (H/2)= (W*H)/4 = (1080x1280)/4</code>,同理 V为<br><code>(W*H)/4 = (1080x1280)/4</code>,因此一张图为 <code>Y+U+V = (1080x1280)*3/2</code>。<br>由于三个部分内部均是行优先存储,三个部分之间是Y,U,V 顺序存储,那么YUV的存储位置如下(PS:后面会用到):<br></p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">Y:<span class="number">0</span> 到 <span class="number">1080</span>*<span class="number">1280</span></div><div class="line">U:<span class="number">1080</span>*<span class="number">1280</span> 到 (<span class="number">1080</span>*<span class="number">1280</span>)*<span class="number">5</span>/<span class="number">4</span></div><div class="line">V:(<span class="number">1080</span>*<span class="number">1280</span>)*<span class="number">5</span>/<span class="number">4</span> 到 (<span class="number">1080</span>*<span class="number">1280</span>)*<span class="number">3</span>/<span class="number">2</span></div></pre></td></tr></table></figure><p></p><h2 id="WEBGL"><a href="#WEBGL" class="headerlink" title="WEBGL"></a>WEBGL</h2><h4 id="WEBGL-是什么"><a href="#WEBGL-是什么" class="headerlink" title="WEBGL 是什么"></a>WEBGL 是什么</h4><blockquote><p>简单来说,WebGL是一项用来在网页上绘制和渲染复杂3D图形,并允许用户与之交互的技术。</p></blockquote><h4 id="WEBGL-组成"><a href="#WEBGL-组成" class="headerlink" title="WEBGL 组成"></a>WEBGL 组成</h4><p>在 webgl 世界中,能绘制的基本图形元素只有点、线、三角形,每个图像都是由大大小小的三角形组成,如下图,无论是多么复杂的图形,其基本组成部分都是由三角形组成。</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-8733acc2e8babedf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="图来源于网络"></p><h4 id="着色器"><a href="#着色器" class="headerlink" title="着色器"></a>着色器</h4><p>着色器是在GPU上运行的程序,是用OpenGL ES着色语言编写的,有点类似 c 语言:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-d4fd034ee984ce88.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>具体的语法可以参考<a href="https://github.com/wshxbqq/GLSL-Card" target="_blank" rel="external">着色器语言 GLSL (opengl-shader-language)入门大全</a>,这里不在多加赘述。</p><p>在 WEBGL 中想要绘制图形就必须要有两个着色器:</p><ul><li>顶点着色器</li><li>片元着色器</li></ul><p>其中顶点着色器的主要功能就是用来处理顶点的,而片元着色器则是用来处理由光栅化阶段生成的每个片元(PS:片元可以理解为像素),最后计算出每个像素的颜色。</p><h4 id="WEBGL-绘制流程"><a href="#WEBGL-绘制流程" class="headerlink" title="WEBGL 绘制流程"></a>WEBGL 绘制流程</h4><p><strong>一、提供顶点坐标</strong><br>因为程序很傻,不知道图形的各个顶点,需要我们自己去提供,顶点坐标可以是自己手动写或者是由软件导出:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-94f2976468d13741.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>在这个图中,我们把顶点写入到缓冲区里,缓冲区对象是WebGL系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。接着我们创建并编译顶点着色器和片元着色器,并用 program 连接两个着色器,并使用。举个例子简单理解下为什么要这样做,我们可以理解成创建Fragment 元素: <code>let f = document.createDocumentFragment()</code>,<br>所有的着色器创建并编译后会处在一种游离的状态,我们需要将他们联系起来,并使用(可以理解成 <code>document.body.appendChild(f)</code>,添加到 body,dom 元素才能被看到,也就是联系并使用)。<br>接着我们还需要将缓冲区与顶点着色器进行连接,这样才能生效。</p><p><strong>二、图元装配</strong><br>我们提供顶点之后,GPU根据我们提供的顶点数量,会挨个执行顶点着色器程序,生成顶点最终的坐标,将图形装配起来。可以理解成制作风筝,就需要将风筝骨架先搭建起来,图元装配就是在这一阶段。</p><p><strong>三、光栅化</strong><br>这一阶段就好比是制作风筝,搭建好风筝骨架后,但是此时却不能飞起来,因为里面都是空的,需要为骨架添加布料。而光栅化就是在这一阶段,将图元装配好的几何图形转成片元(PS: 片元可以理解成像素)。<br><img src="https://upload-images.jianshu.io/upload_images/5308475-7a95aa21909d963f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p><strong>四、着色与渲染</strong><br><img src="https://upload-images.jianshu.io/upload_images/5308475-59cc7412ada8e78c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>着色这一阶段就好比风筝布料搭建完成,但是此时并没有什么图案,需要绘制图案,让风筝更加好看,也就是光栅化后的图形此时并没有颜色,需要经过片元着色器处理,逐片元进行上色并写到颜色缓冲区里,最后在浏览器才能显示有图像的几何图形。</p><p><strong>总结</strong><br>WEBGL 绘制流程可以归纳为以下几点:</p><ol><li>提供顶点坐标(需要我们提供)</li><li>图元装配(按图元类型组装成图形)</li><li>光栅化(将图元装配好的图形,生成像素点)</li><li>提供颜色值(可以动态计算,像素着色)</li><li>通过 canvas 绘制在浏览器上。</li></ol><p><img src="https://upload-images.jianshu.io/upload_images/5308475-16f9f049fed8e951.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><h4 id="WEBGL-YUV-绘制图像思路"><a href="#WEBGL-YUV-绘制图像思路" class="headerlink" title="WEBGL YUV 绘制图像思路"></a>WEBGL YUV 绘制图像思路</h4><p>由于每个视频帧的图像都不太一样,我们肯定不可能知道那么多顶点,那么我们怎么将视频帧的图像用 webgl 画出来呢?这里使用了一个技巧—纹理映射。简单来说就是将一张图像贴在一个几何图形表面,使几何图形看起来像是有图像的几何图形,也就是将纹理坐标和 webgl 系统坐标进行一一对应:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-929e48871f354d38.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>如上图,上面那个是纹理坐标,分为 s 和 t 坐标(或者叫 uv 坐标),值的范围在【0,1】之间,值和图像大小、分辨率无关。下面那张图是webgl坐标系统,是一个三维的坐标系统,这里声明了四个顶点,用两个三角形组装成一个长方形,然后将纹理坐标的顶点与 webgl 坐标系进行一一对应,最终传给片元着色器,片元着色器提取图片的一个个纹素颜色,输出在颜色缓冲区里,最终绘制在浏览器里(PS:纹素你可以理解为组成纹理图像的像素)。但是如果按图上进行一一对应的话,成像会是反的,因为 canvas 的图像坐标,默认(0,0)是在左上角:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-85d302e14e2d61ee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>而纹理坐标则是在左下角,所以绘制时成像就会倒立,解决方法有两种:</p><ul><li><p>对纹理图像进行 Y 轴翻转,webgl 提供了api:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 1代表对纹理图像进行y轴反转</span></div><div class="line">gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, <span class="number">1</span>);</div></pre></td></tr></table></figure></li><li><p>纹理坐标和 webgl 坐标映射进行倒转,举个栗子🌰,如上图所示,本来的纹理坐标<code>(0.0,1.0)</code>对应的是webgl 坐标<code>(-1.0,1.0,0.0)</code>,<code>(0.0,0.0)</code>对应的是<code>(-1.0,-1.0,0.0)</code>,那么我们倒转过来,<code>(0.0,1.0)</code>对应的是<code>(-1.0,-1.0,0.0)</code>,而<code>(0.0,0.0)</code>对应的是<code>(-1.0,1.0,0.0)</code>,这样在浏览器成像就不会是反的。</p></li></ul><p><strong>详细步骤</strong></p><ul><li><p>着色器部分</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 顶点着色器vertexShader</span></div><div class="line">attribute lowp vec4 a_vertexPosition; <span class="comment">// 通过 js 传递顶点坐标</span></div><div class="line">attribute vec2 a_texturePosition; <span class="comment">// 通过 js 传递纹理坐标</span></div><div class="line">varying vec2 v_texCoord; <span class="comment">// 传递纹理坐标给片元着色器</span></div><div class="line"><span class="keyword">void</span> main(){</div><div class="line"> gl_Position=a_vertexPosition;<span class="comment">// 设置顶点坐标</span></div><div class="line"> v_texCoord=a_texturePosition;<span class="comment">// 设置纹理坐标</span></div><div class="line">}</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">// 片元着色器fragmentShader</span></div><div class="line">precision lowp float;<span class="comment">// lowp代表计算精度,考虑节约性能使用了最低精度</span></div><div class="line">uniform sampler2D samplerY;<span class="comment">// sampler2D是取样器类型,图片纹理最终存储在该类型对象中</span></div><div class="line">uniform sampler2D samplerU;<span class="comment">// sampler2D是取样器类型,图片纹理最终存储在该类型对象中</span></div><div class="line">uniform sampler2D samplerV;<span class="comment">// sampler2D是取样器类型,图片纹理最终存储在该类型对象中</span></div><div class="line">varying vec2 v_texCoord; <span class="comment">// 接受顶点着色器传来的纹理坐标</span></div><div class="line"><span class="keyword">void</span> main(){</div><div class="line"> float r,g,b,y,u,v,fYmul;</div><div class="line"> y = texture2D(samplerY, v_texCoord).r;</div><div class="line"> u = texture2D(samplerU, v_texCoord).r;</div><div class="line"> v = texture2D(samplerV, v_texCoord).r;</div><div class="line"> </div><div class="line"> <span class="comment">// YUV420P 转 RGB </span></div><div class="line"> fYmul = y * <span class="number">1.1643828125</span>;</div><div class="line"> r = fYmul + <span class="number">1.59602734375</span> * v - <span class="number">0.870787598</span>;</div><div class="line"> g = fYmul - <span class="number">0.39176171875</span> * u - <span class="number">0.81296875</span> * v + <span class="number">0.52959375</span>;</div><div class="line"> b = fYmul + <span class="number">2.01723046875</span> * u - <span class="number">1.081389160375</span>;</div><div class="line"> gl_FragColor = vec4(r, g, b, <span class="number">1.0</span>);</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>创建并编译着色器,将顶点着色器和片段着色器连接到 program,并使用:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> vertexShader=<span class="keyword">this</span>._compileShader(vertexShaderSource,gl.VERTEX_SHADER);<span class="comment">// 创建并编译顶点着色器</span></div><div class="line"><span class="keyword">let</span> fragmentShader=<span class="keyword">this</span>._compileShader(fragmentShaderSource,gl.FRAGMENT_SHADER);<span class="comment">// 创建并编译片元着色器</span></div><div class="line"></div><div class="line"><span class="keyword">let</span> program=<span class="keyword">this</span>._createProgram(vertexShader,fragmentShader);<span class="comment">// 创建program并连接着色器</span></div></pre></td></tr></table></figure></li><li><p>创建缓冲区,存顶点和纹理坐标(PS:缓冲区对象是WebGL系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用)。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> vertexBuffer = gl.createBuffer();</div><div class="line"><span class="keyword">let</span> vertexRectangle = <span class="keyword">new</span> <span class="built_in">Float32Array</span>([</div><div class="line"> <span class="number">1.0</span>,</div><div class="line"> <span class="number">1.0</span>,</div><div class="line"> <span class="number">0.0</span>,</div><div class="line"> <span class="number">-1.0</span>,</div><div class="line"> <span class="number">1.0</span>,</div><div class="line"> <span class="number">0.0</span>,</div><div class="line"> <span class="number">1.0</span>,</div><div class="line"> <span class="number">-1.0</span>,</div><div class="line"> <span class="number">0.0</span>,</div><div class="line"> <span class="number">-1.0</span>,</div><div class="line"> <span class="number">-1.0</span>,</div><div class="line"> <span class="number">0.0</span></div><div class="line">]);</div><div class="line">gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);</div><div class="line"><span class="comment">// 向缓冲区写入数据</span></div><div class="line">gl.bufferData(gl.ARRAY_BUFFER, vertexRectangle, gl.STATIC_DRAW);</div><div class="line"><span class="comment">// 找到顶点的位置</span></div><div class="line"><span class="keyword">let</span> vertexPositionAttribute = gl.getAttribLocation(program, <span class="string">'a_vertexPosition'</span>);</div><div class="line"><span class="comment">// 告诉显卡从当前绑定的缓冲区中读取顶点数据</span></div><div class="line">gl.vertexAttribPointer(vertexPositionAttribute, <span class="number">3</span>, gl.FLOAT, <span class="literal">false</span>, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line"><span class="comment">// 连接vertexPosition 变量与分配给它的缓冲区对象</span></div><div class="line">gl.enableVertexAttribArray(vertexPositionAttribute);</div><div class="line"></div><div class="line"><span class="comment">// 声明纹理坐标</span></div><div class="line"><span class="keyword">let</span> textureRectangle = <span class="keyword">new</span> <span class="built_in">Float32Array</span>([<span class="number">1.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>, <span class="number">1.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>]);</div><div class="line"><span class="keyword">let</span> textureBuffer = gl.createBuffer();</div><div class="line">gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);</div><div class="line">gl.bufferData(gl.ARRAY_BUFFER, textureRectangle, gl.STATIC_DRAW);</div><div class="line"><span class="keyword">let</span> textureCoord = gl.getAttribLocation(program, <span class="string">'a_texturePosition'</span>);</div><div class="line">gl.vertexAttribPointer(textureCoord, <span class="number">2</span>, gl.FLOAT, <span class="literal">false</span>, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line">gl.enableVertexAttribArray(textureCoord);</div></pre></td></tr></table></figure></li><li><p>初始化并激活纹理单元(YUV)</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//激活指定的纹理单元</span></div><div class="line">gl.activeTexture(gl.TEXTURE0);</div><div class="line">gl.y=<span class="keyword">this</span>._createTexture(); <span class="comment">// 创建纹理</span></div><div class="line">gl.uniform1i(gl.getUniformLocation(program,<span class="string">'samplerY'</span>),<span class="number">0</span>);<span class="comment">//获取samplerY变量的存储位置,指定纹理单元编号0将纹理对象传递给samplerY</span></div><div class="line"></div><div class="line">gl.activeTexture(gl.TEXTURE1);</div><div class="line">gl.u=<span class="keyword">this</span>._createTexture();</div><div class="line">gl.uniform1i(gl.getUniformLocation(program,<span class="string">'samplerU'</span>),<span class="number">1</span>);<span class="comment">//获取samplerU变量的存储位置,指定纹理单元编号1将纹理对象传递给samplerU</span></div><div class="line"></div><div class="line">gl.activeTexture(gl.TEXTURE2);</div><div class="line">gl.v=<span class="keyword">this</span>._createTexture();</div><div class="line">gl.uniform1i(gl.getUniformLocation(program,<span class="string">'samplerV'</span>),<span class="number">2</span>);<span class="comment">//获取samplerV变量的存储位置,指定纹理单元编号2将纹理对象传递给samplerV</span></div></pre></td></tr></table></figure></li><li><p>渲染绘制(PS:由于我们获取到的数据是YUV420P,那么计算方法可以参考刚才说的计算方式)。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div></pre></td><td class="code"><pre><div class="line"> <span class="comment">// 设置清空颜色缓冲时的颜色值</span></div><div class="line"> gl.clearColor(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line"> <span class="comment">// 清空缓冲</span></div><div class="line"> gl.clear(gl.COLOR_BUFFER_BIT);</div><div class="line"></div><div class="line"><span class="keyword">let</span> uOffset = width * height;</div><div class="line"><span class="keyword">let</span> vOffset = (width >> <span class="number">1</span>) * (height >> <span class="number">1</span>);</div><div class="line"></div><div class="line">gl.bindTexture(gl.TEXTURE_2D, gl.y);</div><div class="line"><span class="comment">// 填充Y纹理,Y 的宽度和高度就是 width,和 height,存储的位置就是data.subarray(0, width * height)</span></div><div class="line">gl.texImage2D(</div><div class="line"> gl.TEXTURE_2D,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> width,</div><div class="line"> height,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> gl.UNSIGNED_BYTE,</div><div class="line"> data.subarray(<span class="number">0</span>, uOffset)</div><div class="line">);</div><div class="line"></div><div class="line">gl.bindTexture(gl.TEXTURE_2D, gl.u);</div><div class="line"><span class="comment">// 填充U纹理,Y 的宽度和高度就是 width/2 和 height/2,存储的位置就是data.subarray(width * height, width/2 * height/2 + width * height)</span></div><div class="line">gl.texImage2D(</div><div class="line"> gl.TEXTURE_2D,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> width >> <span class="number">1</span>,</div><div class="line"> height >> <span class="number">1</span>,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> gl.UNSIGNED_BYTE,</div><div class="line"> data.subarray(uOffset, uOffset + vOffset)</div><div class="line">);</div><div class="line"></div><div class="line">gl.bindTexture(gl.TEXTURE_2D, gl.v);</div><div class="line"><span class="comment">// 填充U纹理,Y 的宽度和高度就是 width/2 和 height/2,存储的位置就是data.subarray(width/2 * height/2 + width * height, data.length)</span></div><div class="line">gl.texImage2D(</div><div class="line"> gl.TEXTURE_2D,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> width >> <span class="number">1</span>,</div><div class="line"> height >> <span class="number">1</span>,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> gl.UNSIGNED_BYTE,</div><div class="line"> data.subarray(uOffset + vOffset, data.length)</div><div class="line">);</div><div class="line"></div><div class="line">gl.drawArrays(gl.TRIANGLE_STRIP, <span class="number">0</span>, <span class="number">4</span>); <span class="comment">// 绘制四个点,也就是长方形</span></div></pre></td></tr></table></figure></li></ul><p>上述那些步骤最终可以绘制成这张图:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-907b6837bcda8be5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p><strong>完整代码:</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div><div class="line">136</div><div class="line">137</div><div class="line">138</div><div class="line">139</div><div class="line">140</div><div class="line">141</div><div class="line">142</div><div class="line">143</div><div class="line">144</div><div class="line">145</div><div class="line">146</div><div class="line">147</div><div class="line">148</div><div class="line">149</div><div class="line">150</div><div class="line">151</div><div class="line">152</div><div class="line">153</div><div class="line">154</div><div class="line">155</div><div class="line">156</div><div class="line">157</div><div class="line">158</div><div class="line">159</div><div class="line">160</div><div class="line">161</div><div class="line">162</div><div class="line">163</div><div class="line">164</div><div class="line">165</div><div class="line">166</div><div class="line">167</div><div class="line">168</div><div class="line">169</div><div class="line">170</div><div class="line">171</div><div class="line">172</div><div class="line">173</div><div class="line">174</div><div class="line">175</div><div class="line">176</div><div class="line">177</div><div class="line">178</div><div class="line">179</div><div class="line">180</div><div class="line">181</div><div class="line">182</div><div class="line">183</div><div class="line">184</div><div class="line">185</div><div class="line">186</div><div class="line">187</div><div class="line">188</div><div class="line">189</div><div class="line">190</div><div class="line">191</div><div class="line">192</div><div class="line">193</div><div class="line">194</div><div class="line">195</div><div class="line">196</div><div class="line">197</div><div class="line">198</div><div class="line">199</div><div class="line">200</div><div class="line">201</div><div class="line">202</div><div class="line">203</div><div class="line">204</div><div class="line">205</div><div class="line">206</div><div class="line">207</div><div class="line">208</div><div class="line">209</div><div class="line">210</div><div class="line">211</div><div class="line">212</div><div class="line">213</div><div class="line">214</div><div class="line">215</div><div class="line">216</div><div class="line">217</div><div class="line">218</div><div class="line">219</div><div class="line">220</div><div class="line">221</div><div class="line">222</div><div class="line">223</div><div class="line">224</div><div class="line">225</div><div class="line">226</div><div class="line">227</div><div class="line">228</div><div class="line">229</div><div class="line">230</div><div class="line">231</div><div class="line">232</div><div class="line">233</div><div class="line">234</div><div class="line">235</div><div class="line">236</div><div class="line">237</div><div class="line">238</div><div class="line">239</div><div class="line">240</div><div class="line">241</div><div class="line">242</div><div class="line">243</div><div class="line">244</div><div class="line">245</div><div class="line">246</div><div class="line">247</div><div class="line">248</div><div class="line">249</div><div class="line">250</div><div class="line">251</div><div class="line">252</div><div class="line">253</div><div class="line">254</div><div class="line">255</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">WebglScreen</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(canvas) {</div><div class="line"> <span class="keyword">this</span>.canvas = canvas;</div><div class="line"> <span class="keyword">this</span>.gl = canvas.getContext(<span class="string">'webgl'</span>) || canvas.getContext(<span class="string">'experimental-webgl'</span>);</div><div class="line"> <span class="keyword">this</span>._init();</div><div class="line"> }</div><div class="line"></div><div class="line"> _init() {</div><div class="line"> <span class="keyword">let</span> gl = <span class="keyword">this</span>.gl;</div><div class="line"> <span class="keyword">if</span> (!gl) {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'gl not support!'</span>);</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> <span class="comment">// 图像预处理</span></div><div class="line"> gl.pixelStorei(gl.UNPACK_ALIGNMENT, <span class="number">1</span>);</div><div class="line"> <span class="comment">// GLSL 格式的顶点着色器代码</span></div><div class="line"> <span class="keyword">let</span> vertexShaderSource = <span class="string">`</span></div><div class="line"> attribute lowp vec4 a_vertexPosition;</div><div class="line"> attribute vec2 a_texturePosition;</div><div class="line"> varying vec2 v_texCoord;</div><div class="line"> void main() {</div><div class="line"> gl_Position = a_vertexPosition;</div><div class="line"> v_texCoord = a_texturePosition;</div><div class="line"> }</div><div class="line"> `;</div><div class="line"></div><div class="line"> <span class="keyword">let</span> fragmentShaderSource = <span class="string">`</span></div><div class="line"> precision lowp float;</div><div class="line"> uniform sampler2D samplerY;</div><div class="line"> uniform sampler2D samplerU;</div><div class="line"> uniform sampler2D samplerV;</div><div class="line"> varying vec2 v_texCoord;</div><div class="line"> void main() {</div><div class="line"> float r,g,b,y,u,v,fYmul;</div><div class="line"> y = texture2D(samplerY, v_texCoord).r;</div><div class="line"> u = texture2D(samplerU, v_texCoord).r;</div><div class="line"> v = texture2D(samplerV, v_texCoord).r;</div><div class="line"></div><div class="line"> fYmul = y * 1.1643828125;</div><div class="line"> r = fYmul + 1.59602734375 * v - 0.870787598;</div><div class="line"> g = fYmul - 0.39176171875 * u - 0.81296875 * v + 0.52959375;</div><div class="line"> b = fYmul + 2.01723046875 * u - 1.081389160375;</div><div class="line"> gl_FragColor = vec4(r, g, b, 1.0);</div><div class="line"> }</div><div class="line"> `;</div><div class="line"></div><div class="line"> <span class="keyword">let</span> vertexShader = <span class="keyword">this</span>._compileShader(vertexShaderSource, gl.VERTEX_SHADER);</div><div class="line"> <span class="keyword">let</span> fragmentShader = <span class="keyword">this</span>._compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER);</div><div class="line"></div><div class="line"> <span class="keyword">let</span> program = <span class="keyword">this</span>._createProgram(vertexShader, fragmentShader);</div><div class="line"></div><div class="line"> <span class="keyword">this</span>._initVertexBuffers(program);</div><div class="line"></div><div class="line"> <span class="comment">// 激活指定的纹理单元</span></div><div class="line"> gl.activeTexture(gl.TEXTURE0);</div><div class="line"> gl.y = <span class="keyword">this</span>._createTexture();</div><div class="line"> gl.uniform1i(gl.getUniformLocation(program, <span class="string">'samplerY'</span>), <span class="number">0</span>);</div><div class="line"></div><div class="line"> gl.activeTexture(gl.TEXTURE1);</div><div class="line"> gl.u = <span class="keyword">this</span>._createTexture();</div><div class="line"> gl.uniform1i(gl.getUniformLocation(program, <span class="string">'samplerU'</span>), <span class="number">1</span>);</div><div class="line"></div><div class="line"> gl.activeTexture(gl.TEXTURE2);</div><div class="line"> gl.v = <span class="keyword">this</span>._createTexture();</div><div class="line"> gl.uniform1i(gl.getUniformLocation(program, <span class="string">'samplerV'</span>), <span class="number">2</span>);</div><div class="line"> }</div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 初始化顶点 buffer</div><div class="line"> * @param {glProgram} program 程序</div><div class="line"> */</div><div class="line"></div><div class="line"> _initVertexBuffers(program) {</div><div class="line"> <span class="keyword">let</span> gl = <span class="keyword">this</span>.gl;</div><div class="line"> <span class="keyword">let</span> vertexBuffer = gl.createBuffer();</div><div class="line"> <span class="keyword">let</span> vertexRectangle = <span class="keyword">new</span> <span class="built_in">Float32Array</span>([</div><div class="line"> <span class="number">1.0</span>,</div><div class="line"> <span class="number">1.0</span>,</div><div class="line"> <span class="number">0.0</span>,</div><div class="line"> <span class="number">-1.0</span>,</div><div class="line"> <span class="number">1.0</span>,</div><div class="line"> <span class="number">0.0</span>,</div><div class="line"> <span class="number">1.0</span>,</div><div class="line"> <span class="number">-1.0</span>,</div><div class="line"> <span class="number">0.0</span>,</div><div class="line"> <span class="number">-1.0</span>,</div><div class="line"> <span class="number">-1.0</span>,</div><div class="line"> <span class="number">0.0</span></div><div class="line"> ]);</div><div class="line"> gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);</div><div class="line"> <span class="comment">// 向缓冲区写入数据</span></div><div class="line"> gl.bufferData(gl.ARRAY_BUFFER, vertexRectangle, gl.STATIC_DRAW);</div><div class="line"> <span class="comment">// 找到顶点的位置</span></div><div class="line"> <span class="keyword">let</span> vertexPositionAttribute = gl.getAttribLocation(program, <span class="string">'a_vertexPosition'</span>);</div><div class="line"> <span class="comment">// 告诉显卡从当前绑定的缓冲区中读取顶点数据</span></div><div class="line"> gl.vertexAttribPointer(vertexPositionAttribute, <span class="number">3</span>, gl.FLOAT, <span class="literal">false</span>, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line"> <span class="comment">// 连接vertexPosition 变量与分配给它的缓冲区对象</span></div><div class="line"> gl.enableVertexAttribArray(vertexPositionAttribute);</div><div class="line"></div><div class="line"> <span class="keyword">let</span> textureRectangle = <span class="keyword">new</span> <span class="built_in">Float32Array</span>([<span class="number">1.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>, <span class="number">1.0</span>, <span class="number">0.0</span>, <span class="number">1.0</span>]);</div><div class="line"> <span class="keyword">let</span> textureBuffer = gl.createBuffer();</div><div class="line"> gl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);</div><div class="line"> gl.bufferData(gl.ARRAY_BUFFER, textureRectangle, gl.STATIC_DRAW);</div><div class="line"> <span class="keyword">let</span> textureCoord = gl.getAttribLocation(program, <span class="string">'a_texturePosition'</span>);</div><div class="line"> gl.vertexAttribPointer(textureCoord, <span class="number">2</span>, gl.FLOAT, <span class="literal">false</span>, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line"> gl.enableVertexAttribArray(textureCoord);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 创建并编译一个着色器</div><div class="line"> * @param {string} shaderSource GLSL 格式的着色器代码</div><div class="line"> * @param {number} shaderType 着色器类型, VERTEX_SHADER 或 FRAGMENT_SHADER。</div><div class="line"> * @return {glShader} 着色器。</div><div class="line"> */</div><div class="line"> _compileShader(shaderSource, shaderType) {</div><div class="line"> <span class="comment">// 创建着色器程序</span></div><div class="line"> <span class="keyword">let</span> shader = <span class="keyword">this</span>.gl.createShader(shaderType);</div><div class="line"> <span class="comment">// 设置着色器的源码</span></div><div class="line"> <span class="keyword">this</span>.gl.shaderSource(shader, shaderSource);</div><div class="line"> <span class="comment">// 编译着色器</span></div><div class="line"> <span class="keyword">this</span>.gl.compileShader(shader);</div><div class="line"> <span class="keyword">const</span> success = <span class="keyword">this</span>.gl.getShaderParameter(shader, <span class="keyword">this</span>.gl.COMPILE_STATUS);</div><div class="line"> <span class="keyword">if</span> (!success) {</div><div class="line"> <span class="keyword">let</span> err = <span class="keyword">this</span>.gl.getShaderInfoLog(shader);</div><div class="line"> <span class="keyword">this</span>.gl.deleteShader(shader);</div><div class="line"> <span class="built_in">console</span>.error(<span class="string">'could not compile shader'</span>, err);</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> shader;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 从 2 个着色器中创建一个程序</div><div class="line"> * @param {glShader} vertexShader 顶点着色器。</div><div class="line"> * @param {glShader} fragmentShader 片断着色器。</div><div class="line"> * @return {glProgram} 程序</div><div class="line"> */</div><div class="line"> _createProgram(vertexShader, fragmentShader) {</div><div class="line"> <span class="keyword">const</span> gl = <span class="keyword">this</span>.gl;</div><div class="line"> <span class="keyword">let</span> program = gl.createProgram();</div><div class="line"></div><div class="line"> <span class="comment">// 附上着色器</span></div><div class="line"> gl.attachShader(program, vertexShader);</div><div class="line"> gl.attachShader(program, fragmentShader);</div><div class="line"></div><div class="line"> gl.linkProgram(program);</div><div class="line"> <span class="comment">// 将 WebGLProgram 对象添加到当前的渲染状态中</span></div><div class="line"> gl.useProgram(program);</div><div class="line"> <span class="keyword">const</span> success = <span class="keyword">this</span>.gl.getProgramParameter(program, <span class="keyword">this</span>.gl.LINK_STATUS);</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (!success) {</div><div class="line"> <span class="built_in">console</span>.err(<span class="string">'program fail to link'</span> + <span class="keyword">this</span>.gl.getShaderInfoLog(program));</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> program;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 设置纹理</div><div class="line"> */</div><div class="line"> _createTexture(filter = <span class="keyword">this</span>.gl.LINEAR) {</div><div class="line"> <span class="keyword">let</span> gl = <span class="keyword">this</span>.gl;</div><div class="line"> <span class="keyword">let</span> t = gl.createTexture();</div><div class="line"> <span class="comment">// 将给定的 glTexture 绑定到目标(绑定点</span></div><div class="line"> gl.bindTexture(gl.TEXTURE_2D, t);</div><div class="line"> <span class="comment">// 纹理包装 参考https://github.com/fem-d/webGL/blob/master/blog/WebGL基础学习篇(Lesson%207).md -> Texture wrapping</span></div><div class="line"> gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);</div><div class="line"> gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);</div><div class="line"> <span class="comment">// 设置纹理过滤方式</span></div><div class="line"> gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);</div><div class="line"> gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filter);</div><div class="line"> <span class="keyword">return</span> t;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 渲染图片出来</div><div class="line"> * @param {number} width 宽度</div><div class="line"> * @param {number} height 高度</div><div class="line"> */</div><div class="line"> renderImg(width, height, data) {</div><div class="line"> <span class="keyword">let</span> gl = <span class="keyword">this</span>.gl;</div><div class="line"> <span class="comment">// 设置视口,即指定从标准设备到窗口坐标的x、y仿射变换</span></div><div class="line"> gl.viewport(<span class="number">0</span>, <span class="number">0</span>, gl.canvas.width, gl.canvas.height);</div><div class="line"> <span class="comment">// 设置清空颜色缓冲时的颜色值</span></div><div class="line"> gl.clearColor(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line"> <span class="comment">// 清空缓冲</span></div><div class="line"> gl.clear(gl.COLOR_BUFFER_BIT);</div><div class="line"></div><div class="line"> <span class="keyword">let</span> uOffset = width * height;</div><div class="line"> <span class="keyword">let</span> vOffset = (width >> <span class="number">1</span>) * (height >> <span class="number">1</span>);</div><div class="line"></div><div class="line"> gl.bindTexture(gl.TEXTURE_2D, gl.y);</div><div class="line"> <span class="comment">// 填充纹理</span></div><div class="line"> gl.texImage2D(</div><div class="line"> gl.TEXTURE_2D,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> width,</div><div class="line"> height,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> gl.UNSIGNED_BYTE,</div><div class="line"> data.subarray(<span class="number">0</span>, uOffset)</div><div class="line"> );</div><div class="line"></div><div class="line"> gl.bindTexture(gl.TEXTURE_2D, gl.u);</div><div class="line"> gl.texImage2D(</div><div class="line"> gl.TEXTURE_2D,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> width >> <span class="number">1</span>,</div><div class="line"> height >> <span class="number">1</span>,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> gl.UNSIGNED_BYTE,</div><div class="line"> data.subarray(uOffset, uOffset + vOffset)</div><div class="line"> );</div><div class="line"></div><div class="line"> gl.bindTexture(gl.TEXTURE_2D, gl.v);</div><div class="line"> gl.texImage2D(</div><div class="line"> gl.TEXTURE_2D,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> width >> <span class="number">1</span>,</div><div class="line"> height >> <span class="number">1</span>,</div><div class="line"> <span class="number">0</span>,</div><div class="line"> gl.LUMINANCE,</div><div class="line"> gl.UNSIGNED_BYTE,</div><div class="line"> data.subarray(uOffset + vOffset, data.length)</div><div class="line"> );</div><div class="line"></div><div class="line"> gl.drawArrays(gl.TRIANGLE_STRIP, <span class="number">0</span>, <span class="number">4</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">/**</span></div><div class="line"> * 根据重新设置 canvas 大小</div><div class="line"> * @param {number} width 宽度</div><div class="line"> * @param {number} height 高度</div><div class="line"> * @param {number} maxWidth 最大宽度</div><div class="line"> */</div><div class="line"> setSize(width, height, maxWidth) {</div><div class="line"> <span class="keyword">let</span> canvasWidth = <span class="built_in">Math</span>.min(maxWidth, width);</div><div class="line"> <span class="keyword">this</span>.canvas.width = canvasWidth;</div><div class="line"> <span class="keyword">this</span>.canvas.height = canvasWidth * height / width;</div><div class="line"> }</div><div class="line"></div><div class="line"> destroy() {</div><div class="line"> <span class="keyword">const</span> {</div><div class="line"> gl</div><div class="line"> } = <span class="keyword">this</span>;</div><div class="line"></div><div class="line"> gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><p>最后我们来看下效果图:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-dc620d48dd41e1aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><h2 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h2><p>在实际开发过程中,我们测试一些直播流,有时候渲染的时候图像显示是正常的,但是颜色会偏绿,经研究发现,直播流的不同主播的视频宽度是会不一样,比如在主播在 pk 的时候宽度368,热门主播宽度会到 720,小主播宽度是 540,而宽度为 540 的会显示偏绿,具体原因是 webgl 会经过预处理,默认会将以下值设置为 4:<br></p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 图像预处理</span></div><div class="line">gl.pixelStorei(gl.UNPACK_ALIGNMENT, <span class="number">4</span>);</div></pre></td></tr></table></figure><p></p><p>这样默认设置会每行 4 个字节 4 个字节处理,而 Y分量每行的宽度是 540,是 4 的倍数,字节对齐了,所以图像能够正常显示,而 U,V 分量宽度是 <code>540 / 2 = 270</code>,270 不是4 的倍数,字节非对齐,因此色素就会显示偏绿。目前有两种方法可以解决这个问题:</p><ul><li><p>第一个是直接让 webgl 每行 1 个字节 1 个字节处理(对性能有影响):</p><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 图像预处理</span></div><div class="line">gl.pixelStorei(gl.UNPACK_ALIGNMENT, <span class="number">1</span>);</div></pre></td></tr></table></figure></li><li><p>第二个是让获取到的图像的宽度是 8 的倍数,这样就能做到 YUV 字节对齐,就不会显示绿屏,但是不建议这样做, 转的时候CPU占用极大,建议采取第一个方案。</p></li></ul><h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><p><a href="https://www.cnblogs.com/eustoma/p/6664907.html" target="_blank" rel="external">图像视频编码和FFmpeg(2)——YUV格式介绍和应用 - eustoma - 博客园</a><br><a href="http://www.fourcc.org/yuv.php" target="_blank" rel="external">YUV pixel formats</a><br><a href="https://wiki.videolan.org/YUV/" target="_blank" rel="external">https://wiki.videolan.org/YUV/</a><br><a href="https://docs.microsoft.com/zh-cn/previous-versions/ms867704(v=msdn.10" target="_blank" rel="external">使用 8 位 YUV 格式的视频呈现 | Microsoft Docs</a>?redirectedfrom=MSDN)<br><a href="https://www.jianshu.com/p/2933b1cbebf6" target="_blank" rel="external">IOS 视频格式之YUV - 简书</a><br><a href="https://www.cnblogs.com/wanbo/p/6754066.html" target="_blank" rel="external">图解WebGL&Three.js工作原理 - cnwander - 博客园</a></p>]]></content>
<summary type="html">
<hr><blockquote><p>最近团队在用 WASM + FFmpeg 打造一个 WEB 播放器。我们是通过写 C 语言用 FFmpeg 解码视频,通过编译 C 语言转 WASM 运行在浏览器上与 JavaScript 进行通信。默认 FFmpeg 去解码出来的数据是 yuv,而 canvas 只支持渲染 rgb,那么此时我们有两种方法处理这个yuv,第一个使用 FFmpeg 暴露的方法将 yuv 直接转成 rgb 然后给 canvas 进行渲染,第二个使用 webgl 将 yuv 转 rgb ,在 canvas 上渲染。第一个好处是写法很简单,只需 FFmpeg 暴露的方法将 yuv 直接转成 rgb ,缺点呢就是会耗费一定的cpu,第二个好处是会利用 gpu 进行加速,缺点是写法比较繁琐,而且需要熟悉 WEBGL 。考虑到为了减少 cpu 的占用,利用 gpu 进行并行加速,我们采用了第二种方法。</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="webgl" scheme="http://www.shenzekun.cn/tags/webgl/"/>
</entry>
<entry>
<title>页面CPU和内存占用监控可视化Chrome插件-Graph Process</title>
<link href="http://www.shenzekun.cn/%E9%A1%B5%E9%9D%A2CPU%E5%92%8C%E5%86%85%E5%AD%98%E5%8D%A0%E7%94%A8%E7%9B%91%E6%8E%A7%E5%8F%AF%E8%A7%86%E5%8C%96Chrome%E6%8F%92%E4%BB%B6-Graph-Process.html"/>
<id>http://www.shenzekun.cn/页面CPU和内存占用监控可视化Chrome插件-Graph-Process.html</id>
<published>2019-10-16T16:46:03.000Z</published>
<updated>2019-10-16T16:50:47.683Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>写这个插件的原因是最近要对比一下页面的 cpu 和内存占用的性能,本来是想找看看有没有什么软件能够去可视化一下当前标签页的cpu和内存占用,但是发现却找不到这种软件,mac 上有个活动监视器,但是当你开很多标签页的话并不很好的监听当前标签页的 cpu 和内存占用,能看到谷歌浏览器的 rendered 进程,但是谷歌浏览器的 rendered 进程很多你并不知道是哪个:<br><a id="more"></a><br><img src="https://user-gold-cdn.xitu.io/2019/10/12/16dbfbae32e51d21?w=1600&h=1200&f=png&s=342170" alt=""><br>而且也没有可视化进行查看平均的 cpu 和内存占用,后来看到谷歌浏览器有个任务管理器可以查看当前标签页的 cpu 占用和内存占用,于是想到有没有人已经写了这种插件,但是遗憾的是并没有,后面仔细搜索了谷歌浏览器插件开发文档确定想要的功能能实现,于是这个插件就诞生了😀</p></blockquote><h3 id="效果图"><a href="#效果图" class="headerlink" title="效果图"></a>效果图</h3><p><img src="https://user-gold-cdn.xitu.io/2019/10/12/16dbfba33d23ac52?w=669&h=711&f=gif&s=2398079" alt=""></p><h3 id="主要功能"><a href="#主要功能" class="headerlink" title="主要功能"></a>主要功能</h3><p>对当前标签页点击插件图标,会开始对当前的标签页也就是页面的<strong>CPU和内存</strong>进行监控,并生成对应的变化折线图和平均值和表格,平均值如果超过一定范围会有颜色变化。</p><p>有人可能会问页面开启扩展后会对当前页面统计造成影响,其实是不会的,谷歌扩展是独立的进程,不会对当前的页面的 cpu 和内存占用造成影响</p><h3 id="安装插件"><a href="#安装插件" class="headerlink" title="安装插件"></a>安装插件</h3><blockquote><p><strong>由于使用了谷歌浏览器的实验特性,因此插件需要运行在谷歌浏览器开发者版,可以在这里下载</strong><a href="https://www.google.com/intl/zh-CN/chrome/dev/" target="_blank" rel="external">谷歌浏览器开发者版</a></p></blockquote><p>==> <a href="https://chrome.google.com/webstore/detail/graph-process/gbinchaafafhenoibdeblkooihlobnjj?hl=zh-CN" target="_blank" rel="external">插件地址</a></p>]]></content>
<summary type="html">
<hr><blockquote><p>写这个插件的原因是最近要对比一下页面的 cpu 和内存占用的性能,本来是想找看看有没有什么软件能够去可视化一下当前标签页的cpu和内存占用,但是发现却找不到这种软件,mac 上有个活动监视器,但是当你开很多标签页的话并不很好的监听当前标签页的 cpu 和内存占用,能看到谷歌浏览器的 rendered 进程,但是谷歌浏览器的 rendered 进程很多你并不知道是哪个:<br>
</summary>
<category term="工具" scheme="http://www.shenzekun.cn/categories/%E5%B7%A5%E5%85%B7/"/>
<category term="工具" scheme="http://www.shenzekun.cn/tags/%E5%B7%A5%E5%85%B7/"/>
</entry>
<entry>
<title>判断浏览器是否支持 webp 的几种解决方法</title>
<link href="http://www.shenzekun.cn/%E5%88%A4%E6%96%AD%E6%B5%8F%E8%A7%88%E5%99%A8%E6%98%AF%E5%90%A6%E6%94%AF%E6%8C%81-webp-%E7%9A%84%E5%87%A0%E7%A7%8D%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95.html"/>
<id>http://www.shenzekun.cn/判断浏览器是否支持-webp-的几种解决方法.html</id>
<published>2019-06-01T07:21:16.000Z</published>
<updated>2019-06-01T07:22:58.811Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>我们都知道,WebP 是 Google 推出的 WebP 图片格式,它是一种支持有损压缩和无损压缩的图片文件格式,根据Google测试,相同的图片,WebP 格式的图片均能比 PNG,JPG 格式的图片节约不少体积,但是其兼容性不是很好,如下:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-c11554a7e34e2e15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>因此我们需要做一些兼容处理,那么如何判断浏览器支持 webp 呢?下面有几种方法可供参考。</p></blockquote><a id="more"></a><h3 id="方法一"><a href="#方法一" class="headerlink" title="方法一"></a>方法一</h3><p><strong>使用 canvas 的 toDataURL 进行判断</strong></p><p>toDataURL方法在MDN解释如下:</p><blockquote><p>HTMLCanvasElement.toDataURL() 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。</p></blockquote><ul><li>如果画布的高度或宽度是0,那么会返回字符串“data:,”。</li><li>如果传入的类型非“image/png”,但是返回的值以“data:image/png”开头,那么该传入的类型是不支持的。</li><li>Chrome支持“image/webp”类型。</li></ul><p>toDataURL方法将图片转化为包含dataURI的DOMString,通过 base64 编码前面的图片类型值是image/webp进行判断。</p><p>比如在谷歌浏览器使用toDataURL方法转成image/webp:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-3125077e103aebff.png&originHeight=178&originWidth=1190&size=48391&status=done&width=595?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>在 Safari 浏览器使用toDataURL方法转成image/webp:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-9729ceec28fe7a61.png&originHeight=236&originWidth=2530&size=116511&status=done&width=1265?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>可以发现在不支持 webp 的浏览器进行toDataURL,得到的图片类型并不是 webp,因此我们可以通过这个进行判断。</p><p>实现方法:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> isSupportWebp = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="keyword">return</span> <span class="built_in">document</span>.createElement(<span class="string">'canvas'</span>).toDataURL(<span class="string">'image/webp'</span>, <span class="number">0.5</span>).indexOf(<span class="string">'data:image/webp'</span>) === <span class="number">0</span>;</div><div class="line"> } <span class="keyword">catch</span>(err) {</div><div class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">isSupportWebp()</div></pre></td></tr></table></figure><h3 id="方法二"><a href="#方法二" class="headerlink" title="方法二"></a>方法二</h3><p><strong>在服务端根据请求header信息判断浏览器是否支持webp</strong></p><p>谷歌浏览器上请求图片 header是这样的:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-56755fe184d5f265.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>IE 浏览器请求图片 header是这样的:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-5b6d8cdf3623e0dc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>在图片请求发出的时候,Request Headers 里有 Accept,服务端可以根据Accept 里面是否有 image/webp 进行判断。</p><h3 id="方法三"><a href="#方法三" class="headerlink" title="方法三"></a>方法三</h3><p><strong>通过加载一张 webp 图片进行判断</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> supportsWebp = <span class="function">(<span class="params">{ createImageBitmap, Image }</span>) =></span> {</div><div class="line"> <span class="keyword">if</span> (!createImageBitmap || !Image) <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve(<span class="literal">false</span>);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =></span> {</div><div class="line"> <span class="keyword">const</span> image = <span class="keyword">new</span> Image();</div><div class="line"> image.onload = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> createImageBitmap(image)</div><div class="line"> .then(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> resolve(<span class="literal">true</span>);</div><div class="line"> })</div><div class="line"> .catch(<span class="function"><span class="params">()</span> =></span> {</div><div class="line"> resolve(<span class="literal">false</span>);</div><div class="line"> });</div><div class="line"> };</div><div class="line"> image.onerror = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> resolve(<span class="literal">false</span>);</div><div class="line"> };</div><div class="line"> image.src = <span class="string">'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA='</span>;</div><div class="line"> });</div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">const</span> webpIsSupported = <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">let</span> memo = <span class="literal">null</span>;</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="params">()</span> =></span> {</div><div class="line"> <span class="keyword">if</span> (!memo) {</div><div class="line"> memo = supportsWebp(<span class="built_in">window</span>);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> memo;</div><div class="line"> };</div><div class="line">};</div><div class="line"></div><div class="line">webpIsSupported()().then(<span class="function"><span class="params">res</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"是否支持 webp"</span>, res)</div><div class="line">}).catch(<span class="function"><span class="params">err</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.log(err)</div><div class="line">})</div></pre></td></tr></table></figure><p>此方法会加载一张 1x1 的白色的正方形背景图,用来测试浏览器是否支持 webp。</p><p>在 Google 测试代码:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-67d9dd37b5142e24.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>在 Firefox 测试代码:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-ebb41ccb4555c6f3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>在 Safari 测试代码:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-409f2cf13aedf777.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>Google官方文档是这样处理的(先加载一个WebP图片,如果能获取到图片的宽度和高度,就说明是支持WebP的,反之则不支持):</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">check_webp_feature</span>(<span class="params">feature, callback</span>) </span>{</div><div class="line"> <span class="keyword">var</span> kTestImages = {</div><div class="line"> <span class="attr">lossy</span>: <span class="string">"UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA"</span>,</div><div class="line"> <span class="attr">lossless</span>: <span class="string">"UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=="</span>,</div><div class="line"> <span class="attr">alpha</span>: <span class="string">"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA=="</span>,</div><div class="line"> <span class="attr">animation</span>: <span class="string">"UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"</span></div><div class="line"> };</div><div class="line"> <span class="keyword">var</span> img = <span class="keyword">new</span> Image();</div><div class="line"> img.onload = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> result = (img.width > <span class="number">0</span>) && (img.height > <span class="number">0</span>);</div><div class="line"> callback(feature, result);</div><div class="line"> };</div><div class="line"> img.onerror = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> callback(feature, <span class="literal">false</span>);</div><div class="line"> };</div><div class="line"> img.src = <span class="string">"data:image/webp;base64,"</span> + kTestImages[feature];</div><div class="line">}</div></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL" target="_blank" rel="external">HTMLCanvasElement.toDataURL()</a></li><li><a href="https://davidwalsh.name/detect-webp" target="_blank" rel="external">Detect WEBP Support with JavaScript</a></li><li><a href="http://web.jobbole.com/87103/" target="_blank" rel="external">探究WebP一些事儿</a></li></ul>]]></content>
<summary type="html">
<hr><blockquote><p>我们都知道,WebP 是 Google 推出的 WebP 图片格式,它是一种支持有损压缩和无损压缩的图片文件格式,根据Google测试,相同的图片,WebP 格式的图片均能比 PNG,JPG 格式的图片节约不少体积,但是其兼容性不是很好,如下:<br><img src="https://upload-images.jianshu.io/upload_images/5308475-c11554a7e34e2e15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>因此我们需要做一些兼容处理,那么如何判断浏览器支持 webp 呢?下面有几种方法可供参考。</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="js" scheme="http://www.shenzekun.cn/tags/js/"/>
</entry>
<entry>
<title>JavaScript中的Object.freeze与const之间的区别(译)</title>
<link href="http://www.shenzekun.cn/JavaScript%E4%B8%AD%E7%9A%84Object-freeze%E4%B8%8Econst%E4%B9%8B%E9%97%B4%E7%9A%84%E5%8C%BA%E5%88%AB-%E8%AF%91.html"/>
<id>http://www.shenzekun.cn/JavaScript中的Object-freeze与const之间的区别-译.html</id>
<published>2019-05-31T03:58:45.000Z</published>
<updated>2019-05-31T04:03:49.982Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>原文:<a href="https://medium.com/@bolajiayodeji/the-differences-between-object-freeze-vs-const-in-javascript-4eacea534d7c" target="_blank" rel="external">The differences between Object.freeze() vs Const in JavaScript</a><br>作者:<a href="https://medium.com/@bolajiayodeji" target="_blank" rel="external">Bolaji Ayodeji</a><br><strong>本文经授权翻译转载,版权归原作者所有!</strong></p></blockquote><a id="more"></a><p><img src="http://upload-images.jianshu.io/upload_images/5308475-4434cacb12f965be.png&originHeight=1260&originWidth=1600&size=425448&status=done&width=800?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><p>自ES6发布以来,ES6给JavaScript带来了一些新特性和方法。对于JavaScript开发者来说,这些特性能够很好地改善了我们的工作流程以及工作效率,其中的特性就包括 <code>Object.freeze()</code> 方法和 <code>const</code> 。</p><p>一些开发人员特别是新手们会认为这两个功能的工作方式是一样的,但其实并不是。 让我来告诉你<code>Object.freeze()</code> 和 <code>const</code> 是如何不同的。</p><h2 id="综述"><a href="#综述" class="headerlink" title="综述"></a>综述</h2><p><code>const</code> 和 <code>Object.freeze()</code> 完全不同。</p><ul><li><code>const</code> 的行为像 <code>let</code> 。它们唯一的区别是, <code>const</code> 定义了一个无法重新分配的变量。 通过 <code>const</code> 声明的变量是具有块级作用域的,而不是像 <code>var</code> 声明的变量具有函数作用域。</li><li><code>Object.freeze()</code> 接受一个对象作为参数,并返回一个相同的不可变的对象。这就意味着我们不能添加,删除或更改对象的任何属性。</li></ul><blockquote><p>可变对象的属性能够进行更改,而不可变对象在创建对象后不能更改其属性。</p></blockquote><h2 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h2><h3 id="const"><a href="#const" class="headerlink" title="const"></a>const</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = <span class="string">'Bolaji Ayodeji'</span></div><div class="line">user = <span class="string">'Joe Nash'</span></div></pre></td></tr></table></figure><p>这个例子会抛出一个<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function" target="_blank" rel="external">Uncaught TypeError</a>,因为我们正在尝试重新分配使用const关键字声明的变量user,这样做是无效的。</p><p><img src="http://upload-images.jianshu.io/upload_images/5308475-0474708e10fea495.png&originHeight=98&originWidth=423&size=8399&status=done&width=484?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><p>这个例子中使用 let 或者 var 声明是能够正常工作的,但是使用 const 并不能。</p><h3 id="const-的问题"><a href="#const-的问题" class="headerlink" title="const 的问题"></a>const 的问题</h3><p>使用const声明的对象仅能阻止其重新分配,但是并不能使其声明的对象具有不可变性(能够阻止更改其属性)。</p><p>参考以下代码,我们使用const关键字声明了一个变量,并为其分配了一个名为user的对象。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = {</div><div class="line"> <span class="attr">first_name</span>: <span class="string">'bolaji'</span>,</div><div class="line"> <span class="attr">last_name</span>: <span class="string">'ayodeji'</span>,</div><div class="line"> <span class="attr">email</span>: <span class="string">'[email protected]'</span>,</div><div class="line"> <span class="attr">net_worth</span>: <span class="number">2000</span></div><div class="line">}</div><div class="line">user.last_name = <span class="string">'Samson'</span>;</div><div class="line"><span class="comment">// this would work, user is still mutable!</span></div><div class="line">user.net_worth = <span class="number">983265975975950</span>;</div><div class="line"><span class="comment">// this would work too, user is still mutable and getting rich :)!</span></div><div class="line"><span class="built_in">console</span>.log(user); <span class="comment">// user is mutated</span></div></pre></td></tr></table></figure><p><img src="http://upload-images.jianshu.io/upload_images/5308475-c318c29169064851.png&originHeight=248&originWidth=731&size=26255&status=done&width=746?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><p>尽管我们无法重新分配这个名为 user 的变量,但是我们仍然可以改变其对象本身。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = {</div><div class="line"> <span class="attr">user_name</span>: <span class="string">'bolajiayodeji'</span></div><div class="line">}</div><div class="line"><span class="comment">// won't work</span></div></pre></td></tr></table></figure><p><img src="http://upload-images.jianshu.io/upload_images/5308475-c629ecec9f26bf27.png&originHeight=33&originWidth=592&size=4290&status=done&width=746?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><p>我们肯定希望对象具有无法修改或删除的属性。 <code>const</code> 无法实现这样的功能,但是 <code>Object.freeze</code> 可以。</p><h3 id="Object-freeze"><a href="#Object-freeze" class="headerlink" title="Object.freeze()"></a>Object.freeze()</h3><p>要禁用对象的任何更改,我们需要使用 <code>Object.freeze()</code> 。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = {</div><div class="line"> <span class="attr">first_name</span>: <span class="string">'bolaji'</span>,</div><div class="line"> <span class="attr">last_name</span>: <span class="string">'ayodeji'</span>,</div><div class="line"> <span class="attr">email</span>: <span class="string">'[email protected]'</span>,</div><div class="line"> <span class="attr">net_worth</span>: <span class="number">2000</span></div><div class="line">}</div><div class="line"><span class="built_in">Object</span>.freeze(user);</div><div class="line">user.last_name = <span class="string">'Samson'</span>;</div><div class="line"><span class="comment">// this won't work, user is still immutable!</span></div><div class="line">user.net_worth = <span class="number">983265975975950</span>;</div><div class="line"><span class="comment">// this won't work too, user is still immutable and still broke :(!</span></div><div class="line"><span class="built_in">console</span>.log(user); <span class="comment">// user is immutated</span></div></pre></td></tr></table></figure><p><img src="http://upload-images.jianshu.io/upload_images/5308475-423da13f0ab10e02.png&originHeight=289&originWidth=661&size=31952&status=done&width=746?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><h3 id="具有嵌套属性的对象实际上并未冻结"><a href="#具有嵌套属性的对象实际上并未冻结" class="headerlink" title="具有嵌套属性的对象实际上并未冻结"></a>具有嵌套属性的对象实际上并未冻结</h3><p><code>Object.freeze</code> 只是做了层浅冻结,当遇到具有嵌套属性的对象的时候,我们需要递归<code>Object.freeze</code> 来冻结具有嵌套属性的对象。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">const</span> user = {</div><div class="line"> <span class="attr">first_name</span>: <span class="string">'bolaji'</span>,</div><div class="line"> <span class="attr">last_name</span>: <span class="string">'ayodeji'</span>,</div><div class="line"> <span class="attr">contact</span>: {</div><div class="line"> <span class="attr">email</span>: <span class="string">'[email protected]'</span>,</div><div class="line"> <span class="attr">telephone</span>: <span class="number">08109445504</span>,</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="built_in">Object</span>.freeze(user);</div><div class="line">user.last_name = <span class="string">'Samson'</span>;</div><div class="line"><span class="comment">// this won't work, user is still immutable!</span></div><div class="line">user.contact.telephone = <span class="number">07054394926</span>;</div><div class="line"><span class="comment">// this will work because the nested object is not frozen</span></div><div class="line"><span class="built_in">console</span>.log(user);</div></pre></td></tr></table></figure><p><img src="http://upload-images.jianshu.io/upload_images/5308475-e21415d0c1d9bd1d.png&originHeight=428&originWidth=643&size=46108&status=done&width=746?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></p><p>因此,当具有嵌套属性的对象时, <code>Object.freeze()</code> 并不能完全冻结对象。</p><p>要完全冻结具有嵌套属性的对象,您可以编写自己的库或使用已有的库来冻结对象,如<a href="https://github.com/substack/deep-freeze" target="_blank" rel="external">Deepfreeze</a> 或 <a href="https://github.com/immutable-js/immutable-js" target="_blank" rel="external">immutable-js</a></p><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p><code>const</code> 和 <code>Object.freeze()</code> 并不同, <code>const</code> 是防止变量重新分配,而 <code>Object.freeze()</code> 是使对象具有不可变性。</p><p>感谢阅读,干杯!</p>]]></content>
<summary type="html">
<hr><blockquote><p>原文:<a href="https://medium.com/@bolajiayodeji/the-differences-between-object-freeze-vs-const-in-javascript-4eacea534d7c" target="_blank" rel="external">The differences between Object.freeze() vs Const in JavaScript</a><br>作者:<a href="https://medium.com/@bolajiayodeji" target="_blank" rel="external">Bolaji Ayodeji</a><br><strong>本文经授权翻译转载,版权归原作者所有!</strong></p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="js" scheme="http://www.shenzekun.cn/tags/js/"/>
</entry>
<entry>
<title>Git 更新 commit 的内容和多个 commit 合并</title>
<link href="http://www.shenzekun.cn/Git-%E6%9B%B4%E6%96%B0-commit-%E7%9A%84%E5%86%85%E5%AE%B9%E5%92%8C%E5%A4%9A%E4%B8%AA-commit-%E5%90%88%E5%B9%B6.html"/>
<id>http://www.shenzekun.cn/Git-更新-commit-的内容和多个-commit-合并.html</id>
<published>2019-03-03T14:44:05.000Z</published>
<updated>2019-03-03T14:45:30.572Z</updated>
<content type="html"><![CDATA[<hr><a id="more"></a><h3 id="场景1"><a href="#场景1" class="headerlink" title="场景1"></a>场景1</h3><blockquote><p>有时候我们发现 commit 写的不好,想要更改当前分支最近一次的 commit 的内容,我们可以使用:</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git commit --amend</div></pre></td></tr></table></figure><h3 id="场景二"><a href="#场景二" class="headerlink" title="场景二"></a>场景二</h3><blockquote><p>既然可以更改最近一次的 commit,那可能有些人就想要在<strong>当前分支下前几次的 commit 内容进行更改或者进行 commit 合并</strong>,那我们需要怎么做呢?方法如下:(<strong>注意</strong>此操作最好是在自己维护的分支上弄,多人开发的分支就不太适合)</p></blockquote><p>使用<code>git rebase -i xxxx</code>,这里的 i 指的是交互的意思</p><p>现在项目 commit 如下<br><img src="https://upload-images.jianshu.io/upload_images/5308475-2a88748046a183eb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""><br>我现在想要更改第二个 commit 的内容,那我需要知道当前要修改的 commit 的<strong>父亲</strong>的 id,也就是 init game 的 commit id 1801cf5,然后<code>git rebase -i 1801cf5</code>:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-d21bf86786de47b6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>当前是 pick,我们需要使用 reword 来更改第二个 commit 的内容</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-957a03f8285a0c8a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>保存退出,出现</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-e08e80d0a24f15e7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>修改 commit 内容,然后退出,出现</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-62da87efd40fc986.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p><code>git log —oneline</code> 查看</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-eb4b1a44e2e56f61.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>成功添加.号</p><h3 id="场景3"><a href="#场景3" class="headerlink" title="场景3"></a>场景3</h3><blockquote><p>现在我们想要把第二个 commit 和第一个 commit 这<strong>两个连续</strong>的 commit 进行合并,我们可以这样:</p></blockquote><p>同样使用<code>git rebase -i 1801cf5</code>,将第二个 pick 改为 s(squash),这样可以合并到上一个commit</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-5be96305b81e74a7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>保存退出</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-58012f5cadf932b7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>修改成</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-da04f00fa3b300aa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>保存退出</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-b21e56bcdec9e4b0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p><code>git log —oneline</code> 查看</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-eea8faf0138d8a68.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>成功合并。</p><h3 id="场景4"><a href="#场景4" class="headerlink" title="场景4"></a>场景4</h3><blockquote><p>现在有人又有个疑惑,如果我是相隔的 commit,但是我想将<strong>相隔的 commit 的内容</strong>进行合并,那又需怎么做呢?</p></blockquote><p>还是以下面这个为例子<br><img src="https://upload-images.jianshu.io/upload_images/5308475-7ced295741ece98b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>现在我们想将第一个 commit 的内容和第三个内容进行合并,这里你可能你比较疑惑,第三个 commit 的上一个 commit 是没有的,那怎么用 rebase 呢?我们需要记下第三个 commit 的 id,输入<code>git rebase -i 1801cf5</code></p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-eb25a9ef5ddeef43.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>目前只有两个,没有 init game 的 commit id,我们需要将这个 commit id 添加上去(我们可以不用写commit 的内容):</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-8806b9794ce3921a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>将相关要合并的 commit 写在一起:</p><p><img src="https://upload-images.jianshu.io/upload_images/5308475-0b5402b9e6de430b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>保存退出。按照之前的步骤即可。</p>]]></content>
<summary type="html">
<hr>
</summary>
<category term="技巧" scheme="http://www.shenzekun.cn/categories/%E6%8A%80%E5%B7%A7/"/>
<category term="git" scheme="http://www.shenzekun.cn/tags/git/"/>
</entry>
<entry>
<title>使用 VSCode 调试 Koa 或者 Express 项目</title>
<link href="http://www.shenzekun.cn/%E4%BD%BF%E7%94%A8-VSCode-%E8%B0%83%E8%AF%95-Koa-%E6%88%96%E8%80%85-Express.html"/>
<id>http://www.shenzekun.cn/使用-VSCode-调试-Koa-或者-Express.html</id>
<published>2018-12-31T12:22:59.000Z</published>
<updated>2019-05-08T09:42:31.889Z</updated>
<content type="html"><![CDATA[<hr><h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><blockquote><p>平常调试 node 打 log 打习惯了,突然发现一个问题就是打印对象的时候,尤其这个对象里面有很多属性的时候,在终端上得一直往上拉才能看到,因此打算使用 vscode 来打断点调试程序。</p></blockquote><a id="more"></a><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><blockquote><p>这里的例子是使用 koa ,express 类似。我是使用阿里巴巴的<a href="https://alibaba.github.io/ice/" target="_blank" rel="external">飞冰</a>快速搭建一个后台和前台的项目。</p></blockquote><ol><li>下载飞冰</li><li>打开飞冰,使用ICE Design Pro模板并点击 添加koa2,如下<br><img src="http://upload-images.jianshu.io/upload_images/5308475-a67136d084037f23.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image"></li><li>自动安装完成后,使用 vscode 打开项目:<br><img src="http://upload-images.jianshu.io/upload_images/5308475-7e3ddc3492ebe230.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image"></li><li>打开终端,运行<code>npm run client</code> 这个时候前端项目就运行起来了。</li><li>稍微修改一下前端的代码,因为这个模板默认是使用前端直接返回数据,而不去请求接口,打开<code>client/pages/UserLogin/actions.js</code>,将 <code>import {login} from '../../api/user';</code>改为 <code>import {login} from '../../api/index';</code>就可以了。最后打开页面,地址终端里面有说明。</li></ol><h3 id="编写launch-json"><a href="#编写launch-json" class="headerlink" title="编写launch.json"></a>编写launch.json</h3><blockquote><p>VsCode左侧第四个按钮是调试按钮,默认是『没有配置』。点击右侧的齿轮状图标,选择Node.js 会在项目根目录下创建 .vscode 的文件夹及 launch.json 文件。launch.json 内容如下:</p></blockquote><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> // 使用 IntelliSense 了解相关属性。 </div><div class="line"> // 悬停以查看现有属性的描述。</div><div class="line"> // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387</div><div class="line"> "version": "0.2.0",</div><div class="line"> "configurations": [</div><div class="line"> {</div><div class="line"> "type": "node",</div><div class="line"> "request": "launch",</div><div class="line"> "name": "启动程序",</div><div class="line"> "program": "${workspaceFolder}/server/index.js"</div><div class="line"> }</div><div class="line"> ]</div><div class="line">}</div></pre></td></tr></table></figure><p>默认会访问server下的 index.js 文件,但是这个项目的入口文件是 app.js,因此需要将<code>index.js</code>改为<code>app.js</code>。</p><p><strong>在launch.json中会使用到一些预定变量,这里说明一下:</strong></p><ul><li><code>${workspaceRoot}</code>:VSCode中打开文件夹的路径</li><li><code>${workspaceRootFolderName}</code>:VSCode中打开文件夹的路径, 但不包含”/“</li><li><code>${file}</code>:当前打开的文件</li><li><code>${fileBasename}</code>: 当前打开文件的文件名, 不含扩展名</li><li><code>${fileDirname}</code>: 当前打开文件的目录名</li><li><code>${fileExtname}</code> 当前打开文件的扩展名</li><li><code>${cwd}</code>:当前执行目录</li></ul><p>当我们在单步调试程序的时候,会进入node_modules里面,通常情况下我们并不需要去关心里面的逻辑实现,只关心项目本身的代码。在这个时候,我们就需要使用skipFiles:</p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> // 使用 IntelliSense 了解相关属性。</div><div class="line"> // 悬停以查看现有属性的描述。</div><div class="line"> // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387</div><div class="line"> "version": "0.2.0",</div><div class="line"> "configurations": [{</div><div class="line"> "type": "node",</div><div class="line"> "request": "launch",</div><div class="line"> "name": "启动程序",</div><div class="line"> "program": "${workspaceFolder}/server/app.js",</div><div class="line"> "skipFiles": [</div><div class="line"> "${workspaceRoot}/node_modules/**/*.js",</div><div class="line"> "<node_internals>/**/*.js"</div><div class="line"> ]</div><div class="line"> }]</div><div class="line">}</div></pre></td></tr></table></figure><p>我们还想要自动重启的功能,安装 <code>nodemon</code> 或者 <code>node-dev</code>:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">// 任选其一</div><div class="line">npm i nodemon -g</div><div class="line">npm i node-dev -g</div></pre></td></tr></table></figure><p>修改lanuch.json:</p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> // 使用 IntelliSense 了解相关属性。</div><div class="line"> // 悬停以查看现有属性的描述。</div><div class="line"> // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387</div><div class="line"> "version": "0.2.0",</div><div class="line"> "configurations": [{</div><div class="line"> "type": "node",</div><div class="line"> "request": "launch",</div><div class="line"> "name": "启动程序",</div><div class="line"> "program": "${workspaceFolder}/server/app.js",</div><div class="line"> "runtimeExecutable": "nodemon", // 或者 node-dev</div><div class="line"> "restart": true,</div><div class="line"> "console": "integratedTerminal",</div><div class="line"> "skipFiles": [</div><div class="line"> "${workspaceRoot}/node_modules/**/*.js",</div><div class="line"> "<node_internals>/**/*.js"</div><div class="line"> ]</div><div class="line"> }]</div><div class="line">}</div></pre></td></tr></table></figure><p>这里新增了三个字段,分别是:</p><ul><li>runtimeExecutable:用什么命令执行 app.js,这里设置为 nodemon,默认是 node</li><li>restart:在终止 Node.js 后重启会话</li><li>console:启动调试目标的位置,这里选择在 vscode 的集成终端输出信息</li></ul><h3 id="调试"><a href="#调试" class="headerlink" title="调试"></a>调试</h3><p>这里在 <code>server/controller/user.js</code> 的 login 打了个断点:<br><img src="http://upload-images.jianshu.io/upload_images/5308475-95e9b238f28d55cd.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image">启动调试,如下:<br><img src="http://upload-images.jianshu.io/upload_images/5308475-f0ce310223b32c2f.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image"><br>vscode 集成终端打印如下:<br><img src="http://upload-images.jianshu.io/upload_images/5308475-d6512d9a5ec0de37.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image"><br>在前端页面点击登录,会跳到这里:<br><img src="http://upload-images.jianshu.io/upload_images/5308475-2af99b27635d0d62.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image"><br>我们就能看到变量的信息啦😄(<strong>注意</strong>:如果此时终止了调试,nodemon 还是会运行,得在集成终端终止)</p>]]></content>
<summary type="html">
<hr><h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><blockquote><p>平常调试 node 打 log 打习惯了,突然发现一个问题就是打印对象的时候,尤其这个对象里面有很多属性的时候,在终端上得一直往上拉才能看到,因此打算使用 vscode 来打断点调试程序。</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="node" scheme="http://www.shenzekun.cn/tags/node/"/>
</entry>
<entry>
<title>nodejs 路径解析顺序</title>
<link href="http://www.shenzekun.cn/nodejs-%E8%B7%AF%E5%BE%84%E8%A7%A3%E6%9E%90%E9%A1%BA%E5%BA%8F.html"/>
<id>http://www.shenzekun.cn/nodejs-路径解析顺序.html</id>
<published>2018-06-04T11:48:58.000Z</published>
<updated>2018-10-21T12:51:44.839Z</updated>
<content type="html"><![CDATA[<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>平时在使用 nodejs 去 require 的时候是不是有使用绝对路径或者相对路径去引用,那么 nodejs 解析路径顺序是怎么样的呢?接下来我会讲一下 nodejs 路径解析顺序</p></blockquote><a id="more"></a><h2 id="相对路径解析顺序"><a href="#相对路径解析顺序" class="headerlink" title="相对路径解析顺序"></a>相对路径解析顺序</h2><p>假设有一个文件路径为 <code>/root/src/moduleA.js</code>,包含了一个导入<code>var x = require("./moduleB");</code>, 也就是导入了一个相对路径的一个模块,那么Node.js以下面的顺序解析这个导入:</p><ul><li>到<code>/root/src/moduleB.js</code>这个路径是否存在,如果不存在进入下一步。</li><li>检查<code>/root/src/moduleB</code> 目录是否包含一个<code>package.json</code>文件,且<code>package.json</code>文件指定了一个”main”模块,比如 ,Node.js发现文件 <code>/root/src/moduleB/package.json</code> 包含了 <code>{ "main": "lib/mainModule.js" }</code>,那么 nodejs 就会去 <code>/root/src/moduleB/lib/mainModule.js</code></li><li>如果没有 main 字段,nodejs会检查<code>/root/src/moduleB</code>目录是否包含一个 <code>index.js</code> 文件。 这个文件会被隐式地当作那个文件夹下的”main”模块。</li></ul><h2 id="绝对路径解析顺序"><a href="#绝对路径解析顺序" class="headerlink" title="绝对路径解析顺序"></a>绝对路径解析顺序</h2><p>假设有一个文件路径为<code>/root/src/moduleA.js</code>,里面包含了一个导入<code>var x = require("moduleB");</code>,也就是绝对路径的一个模块,那么Node.js以下面的顺序解析这个导入:</p><ul><li><code>/root/src/node_modules/moduleB.js</code></li><li><code>/root/src/node_modules/moduleB/package.json</code>(里面指定了 main 字段,跟上面相对路径是一样的)</li><li><code>/root/src/node_modules/moduleB/index.js</code><br>如果上面三个没有找到,往<strong>上一级</strong>目录找:</li><li><code>/root/node_modules/moduleB.js</code></li><li><code>/root/node_modules/moduleB/package.json</code></li><li><code>/root/node_modules/moduleB/index.js</code><br>如果还没有找到,继续往<strong>上一级</strong>找:</li><li><code>/node_modules/moduleB.js</code></li><li><code>/node_modules/moduleB/package.json</code></li><li><code>/node_modules/moduleB/index.js</code></li></ul>]]></content>
<summary type="html">
<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>平时在使用 nodejs 去 require 的时候是不是有使用绝对路径或者相对路径去引用,那么 nodejs 解析路径顺序是怎么样的呢?接下来我会讲一下 nodejs 路径解析顺序</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="node" scheme="http://www.shenzekun.cn/tags/node/"/>
</entry>
<entry>
<title>四种方法实现数据双向绑定</title>
<link href="http://www.shenzekun.cn/%E5%9B%9B%E7%A7%8D%E6%96%B9%E6%B3%95%E5%AE%9E%E7%8E%B0%E6%95%B0%E6%8D%AE%E5%8F%8C%E5%90%91%E7%BB%91%E5%AE%9A.html"/>
<id>http://www.shenzekun.cn/四种方法实现数据双向绑定.html</id>
<published>2018-05-10T03:29:08.000Z</published>
<updated>2018-10-21T12:51:44.835Z</updated>
<content type="html"><![CDATA[<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>在一些前端框架中,例如 <code>angular</code>,<code>vue</code>都有数据双向数据绑定的功能,这个功能极大的方便我们操作数据。那么接下来我会讲解一下双向数据绑定的4种实现方式。</p></blockquote><a id="more"></a><h2 id="方式"><a href="#方式" class="headerlink" title="方式"></a>方式</h2><h3 id="1-手动触发绑定"><a href="#1-手动触发绑定" class="headerlink" title="1.手动触发绑定"></a>1.手动触发绑定</h3><p>手动触发绑定的主要思路是通过在数据对象定义 get 和 set 方法(可以使用其他的命名方法),调用时手动去触发 get 和 set 方法去获取数据,修改数据,改变数据后会主动去触发 get 和 set 函数中视图层的重新渲染。</p><p>简单的手动触发绑定代码如下:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"ie=edge"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>手动双向绑定<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"input"</span> <span class="attr">s-value</span>=<span class="string">"value"</span>></span></div><div class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"el"</span> <span class="attr">s-text</span>=<span class="string">"value"</span>></span><span class="tag"></<span class="name">span</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"> <span class="keyword">let</span> elems = [<span class="built_in">document</span>.getElementById(<span class="string">'el'</span>), <span class="built_in">document</span>.getElementById(<span class="string">'input'</span>)];</div><div class="line"> <span class="keyword">let</span> data = {</div><div class="line"> <span class="attr">value</span>: <span class="string">''</span></div><div class="line"> }</div><div class="line"> <span class="keyword">let</span> directive = {</div><div class="line"> <span class="attr">text</span>: <span class="function"><span class="keyword">function</span> (<span class="params">text</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.innerHTML = text</div><div class="line"> },</div><div class="line"> <span class="attr">value</span>: <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.setAttribute(<span class="string">'value'</span>, value)</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="comment">// 监听 input 的 keyup 事件</span></div><div class="line"> elems[<span class="number">1</span>].addEventListener(<span class="string">'keyup'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</div><div class="line"> set(<span class="string">'value'</span>, e.target.value)</div><div class="line"> })</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">scan</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> elem <span class="keyword">of</span> elems) {</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> attr <span class="keyword">of</span> elem.attributes) {</div><div class="line"> <span class="keyword">if</span> (attr.nodeName.indexOf(<span class="string">'s-'</span>) !== <span class="number">-1</span>) {</div><div class="line"> <span class="comment">// 调用属性指令</span></div><div class="line"> directive[attr.nodeName.slice(<span class="number">2</span>)].call(elem, data[attr.nodeValue])</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">set</span>(<span class="params">key, value</span>) </span>{</div><div class="line"> data[key] = value;</div><div class="line"> scan();</div><div class="line"> }</div><div class="line"> <span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure><h3 id="2-数据劫持"><a href="#2-数据劫持" class="headerlink" title="2.数据劫持"></a>2.数据劫持</h3><p>数据劫持的基本思路是使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty" target="_blank" rel="external">Object.defineProperty</a> 对 ViewModel 数据对象进行 get 和 set 的监听,当有数据变动的时候扫描元素节点,然后去运行对应节点上的指令(directive)。</p><p>代码如下:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"ie=edge"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>数据劫持<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"input"</span> <span class="attr">s-value</span>=<span class="string">"value"</span>></span></div><div class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"el"</span> <span class="attr">s-text</span>=<span class="string">"value"</span>></span><span class="tag"></<span class="name">span</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"> <span class="keyword">let</span> elems = [<span class="built_in">document</span>.getElementById(<span class="string">'el'</span>), <span class="built_in">document</span>.getElementById(<span class="string">'input'</span>)];</div><div class="line"> <span class="keyword">let</span> data = {</div><div class="line"> <span class="attr">value</span>: <span class="string">''</span></div><div class="line"> }</div><div class="line"> <span class="keyword">let</span> directive = {</div><div class="line"> <span class="attr">text</span>: <span class="function"><span class="keyword">function</span> (<span class="params">text</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.innerHTML = text</div><div class="line"> },</div><div class="line"> <span class="attr">value</span>: <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.setAttribute(<span class="string">'value'</span>, value)</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">let</span> value;</div><div class="line"> defineGetAndSet(data, <span class="string">'value'</span>)</div><div class="line"> <span class="comment">// 监听 input 的 keyup 事件</span></div><div class="line"> elems[<span class="number">1</span>].addEventListener(<span class="string">'keyup'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</div><div class="line"> data.value = e.target.value;</div><div class="line"> })</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">scan</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> elem <span class="keyword">of</span> elems) {</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> attr <span class="keyword">of</span> elem.attributes) {</div><div class="line"> <span class="keyword">if</span> (attr.nodeName.indexOf(<span class="string">'s-'</span>) !== <span class="number">-1</span>) {</div><div class="line"> <span class="comment">// 调用属性指令</span></div><div class="line"> directive[attr.nodeName.slice(<span class="number">2</span>)].call(elem, data[attr.nodeValue])</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">defineGetAndSet</span>(<span class="params">obj, attrName</span>) </span>{</div><div class="line"> <span class="built_in">Object</span>.defineProperty(obj, attrName, {</div><div class="line"> <span class="attr">get</span>: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> value</div><div class="line"> },</div><div class="line"> <span class="attr">set</span>: <span class="function"><span class="keyword">function</span> (<span class="params">newValue</span>) </span>{</div><div class="line"> value = newValue;</div><div class="line"> scan()</div><div class="line"> },</div><div class="line"> <span class="attr">configurable</span>: <span class="literal">true</span>,</div><div class="line"> <span class="attr">enumerable</span>: <span class="literal">true</span></div><div class="line"> })</div><div class="line"> }</div><div class="line"> <span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure><h3 id="3-使用-es6的-Proxy"><a href="#3-使用-es6的-Proxy" class="headerlink" title="3.使用 es6的 Proxy"></a>3.使用 es6的 Proxy</h3><p>利用<a href="http://es6.ruanyifeng.com/#docs/proxy" target="_blank" rel="external">Proxy</a> ,它可以目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此可以对外界的访问进行过滤和改写,实现数据双向数据绑定和上一个类似。</p><p>代码如下:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"ie=edge"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>使用 proxy 进行数据双向绑定<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"input"</span> <span class="attr">s-value</span>=<span class="string">"value"</span>></span></div><div class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"el"</span> <span class="attr">s-text</span>=<span class="string">"value"</span>></span><span class="tag"></<span class="name">span</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"> <span class="keyword">let</span> elems = [<span class="built_in">document</span>.getElementById(<span class="string">'el'</span>), <span class="built_in">document</span>.getElementById(<span class="string">'input'</span>)];</div><div class="line"> <span class="keyword">let</span> directive = {</div><div class="line"> <span class="attr">text</span>: <span class="function"><span class="keyword">function</span> (<span class="params">text</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.innerHTML = text</div><div class="line"> },</div><div class="line"> <span class="attr">value</span>: <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.setAttribute(<span class="string">'value'</span>, value)</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// ------------- 看下面 -------------</span></div><div class="line"> <span class="keyword">let</span> data = <span class="keyword">new</span> <span class="built_in">Proxy</span>({}, {</div><div class="line"> <span class="attr">get</span>: <span class="function"><span class="keyword">function</span> (<span class="params">target, key, receiver</span>) </span>{</div><div class="line"> <span class="keyword">return</span> target.value</div><div class="line"> },</div><div class="line"> set(target, key, value, receiver) {</div><div class="line"> target.value = value;</div><div class="line"> scan();</div><div class="line"> }</div><div class="line"> })</div><div class="line"> <span class="comment">// ------------- 看上面 -------------</span></div><div class="line"></div><div class="line"> <span class="comment">// 监听 input 的 keyup 事件</span></div><div class="line"> elems[<span class="number">1</span>].addEventListener(<span class="string">'keyup'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</div><div class="line"> data.value = e.target.value;</div><div class="line"> })</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">scan</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> elem <span class="keyword">of</span> elems) {</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> attr <span class="keyword">of</span> elem.attributes) {</div><div class="line"> <span class="keyword">if</span> (attr.nodeName.indexOf(<span class="string">'s-'</span>) !== <span class="number">-1</span>) {</div><div class="line"> <span class="comment">// 调用属性指令</span></div><div class="line"> directive[attr.nodeName.slice(<span class="number">2</span>)].call(elem, data[attr.nodeValue])</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure><h3 id="4-脏检查"><a href="#4-脏检查" class="headerlink" title="4. 脏检查"></a>4. 脏检查</h3><p>脏检查的基本原理是在 ViewModel 对象的某个属性值发生变化的时候找到与这个属性值相关的所有元素,然后去比较数据变化,如果变化就用 directive 指令调用,对这个元素进行重新渲染。</p><p>简单的脏检查代码如下:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"ie=edge"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>脏检查<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"input"</span> <span class="attr">s-bind</span>=<span class="string">"value"</span> <span class="attr">s-event</span>=<span class="string">"value"</span>></span></div><div class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"el"</span> <span class="attr">s-event</span>=<span class="string">"text"</span> <span class="attr">s-bind</span>=<span class="string">"value"</span>></span><span class="tag"></<span class="name">span</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"> <span class="keyword">let</span> elems = [<span class="built_in">document</span>.getElementById(<span class="string">'el'</span>), <span class="built_in">document</span>.getElementById(<span class="string">'input'</span>)];</div><div class="line"> <span class="keyword">let</span> directives = {</div><div class="line"> <span class="attr">text</span>: <span class="function"><span class="keyword">function</span> (<span class="params">text</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.innerHTML = text</div><div class="line"> },</div><div class="line"> <span class="attr">value</span>: <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>{</div><div class="line"> <span class="keyword">this</span>.setAttribute(<span class="string">'value'</span>, value)</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">let</span> data = {</div><div class="line"> <span class="attr">value</span>: <span class="string">''</span></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// 扫描元素,使每个元素的 directive 数组为空</span></div><div class="line"> scan(elems)</div><div class="line"></div><div class="line"> <span class="comment">// 监听 input 的 keyup 事件</span></div><div class="line"> elems[<span class="number">1</span>].addEventListener(<span class="string">'keyup'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</div><div class="line"> data.value = e.target.value;</div><div class="line"> startDirtyCheck(e.target.getAttribute(<span class="string">'s-bind'</span>))</div><div class="line"> })</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">scan</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> elem <span class="keyword">of</span> elems) {</div><div class="line"> elem.directive = []</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// 开启脏检查</span></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">startDirtyCheck</span>(<span class="params">value</span>) </span>{</div><div class="line"> <span class="keyword">let</span> list = <span class="built_in">document</span>.querySelectorAll(<span class="string">'[s-bind='</span> + value + <span class="string">']'</span>)</div><div class="line"> dirtyCheck(list)</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">dirtyCheck</span>(<span class="params">elems</span>) </span>{</div><div class="line"> <span class="comment">// 扫描带指令的节点属性</span></div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, len = elems.length; i < len; i++) {</div><div class="line"> <span class="keyword">let</span> elem = elems[i];</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>, len1 = elem.attributes.length; j < len1; j++) {</div><div class="line"> <span class="keyword">let</span> attr = elem.attributes[j];</div><div class="line"> <span class="keyword">if</span> (attr.nodeName.indexOf(<span class="string">'s-event'</span>) !== <span class="number">-1</span>) {</div><div class="line"> <span class="keyword">let</span> dataKey = elem.getAttribute(<span class="string">'s-bind'</span>)</div><div class="line"> <span class="comment">// 进行脏数据检查,如果数据改变,重新执行指令</span></div><div class="line"> <span class="keyword">if</span> (elem.directive[attr.nodeValue] !== data[dataKey]) {</div><div class="line"> directives[attr.nodeValue].call(elem, data[dataKey])</div><div class="line"> elem.directive[attr.nodeValue] = data[dataKey]</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>在一些前端框架中,例如 <code>angular</code>,<code>vue</code>都有数据双向数据绑定的功能,这个功能极大的方便我们操作数据。那么接下来我会讲解一下双向数据绑定的4种实现方式。</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="js" scheme="http://www.shenzekun.cn/tags/js/"/>
<category term="前端" scheme="http://www.shenzekun.cn/tags/%E5%89%8D%E7%AB%AF/"/>
</entry>
<entry>
<title>react-native问题汇总</title>
<link href="http://www.shenzekun.cn/react-native%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB.html"/>
<id>http://www.shenzekun.cn/react-native问题汇总.html</id>
<published>2018-05-08T05:05:13.000Z</published>
<updated>2018-10-22T02:44:58.760Z</updated>
<content type="html"><![CDATA[<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>前些天使用react-native 写了个项目,遇到的问题挺多的,在这里记录下来📝,避免忘记。本篇文章会不定期更新!</p></blockquote><a id="more"></a><h2 id="问题汇总"><a href="#问题汇总" class="headerlink" title="问题汇总"></a>问题汇总</h2><h3 id="问题一"><a href="#问题一" class="headerlink" title="问题一"></a>问题一</h3><p>出现Remote debugger is in a background tab which may cause apps to perform slowly黄色警报<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-043813.png" alt=""></p><p><strong>解决方法</strong><br>把那个chrome的Tab页保持最前,窗口不要最小化就好了。。。。</p><h3 id="问题二"><a href="#问题二" class="headerlink" title="问题二"></a>问题二</h3><p>出现connection to <a href="http://localhost:8081" target="_blank" rel="external">http://localhost:8081</a> 红色错误<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-044331.png" alt=""></p><p><strong>解决方法</strong><br>这个很神奇。遇到了多按 ⌘R几下或者把模拟器上的项目删除之后重新加载一般就会解决。</p><blockquote><p>bad news: metro v0.29.0 won’t work with RN 0.54-0.55 because it introduced a new config param that RN is not handling yet (we’re working on improving configuration compatibility between RN and metro).</p><p>good news: the actual fix needed to solve this issue is in the RN repo (7be3d1c), so cherry-picking it into the 0.54 and 0.55 branches and releasing a RN minor version will fix this. (cc @hramos, @grabbou ).</p></blockquote><p>或者使用下面命令可以解决问题</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">yarn cache clean&&yarn</div></pre></td></tr></table></figure><h3 id="问题三"><a href="#问题三" class="headerlink" title="问题三"></a>问题三</h3><p>出现Runtime is not ready for debugging红色错误<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-044718.png" alt=""></p><p><strong>两种解决方法</strong></p><ol><li>关掉<a href="http://localhost:8081/debugger-ui/" target="_blank" rel="external">http://localhost:8081/debugger-ui/</a> 再重新开启就行了</li><li>按 command+d 将Debug JS Remotely关掉也可以</li></ol><h3 id="问题四"><a href="#问题四" class="headerlink" title="问题四"></a>问题四</h3><p>出现Unrecognized font family 红色报警<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-045023.png" alt=""></p><p><strong>解决方法</strong><br>在终端输入</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">react-native link react-native-vector-icons</div></pre></td></tr></table></figure><p>然后重新启动即可</p><h3 id="问题五"><a href="#问题五" class="headerlink" title="问题五"></a>问题五</h3><p>React Native不支持自动计算Image等View的大小</p><p><a href="http://facebook.github.io/react-native/docs/images.html#why-not-automatically-size-everything" target="_blank" rel="external">详情</a></p><h3 id="问题六"><a href="#问题六" class="headerlink" title="问题六"></a>问题六</h3><p>react-native-interactable 出现 Invariant Violation红色警报<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15257552426894.jpg" alt=""></p><p><strong>解决方法</strong><br>降级:将 react-native 版本降到0.53.0 就行了<br>参考 <a href="https://github.com/wix/react-native-interactable/issues/185" target="_blank" rel="external">wix/react-native-interactable#185</a></p><h3 id="问题七"><a href="#问题七" class="headerlink" title="问题七"></a>问题七</h3><p>Build后遇到’No bundle URL present’ error<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-045522.png" alt=""></p><p><strong>解决方法</strong><br>关闭SS,VPN这类的服务,重新 <code>react-native run-ios</code> 即可。很神奇。。。</p><p>官方也有这个 <a href="https://github.com/facebook/react-native/issues/12754" target="_blank" rel="external">issue</a></p><h3 id="问题八"><a href="#问题八" class="headerlink" title="问题八"></a>问题八</h3><p>出现regeneratorRuntime is not defined<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-045839.png" alt=""></p><p><strong>解决方法</strong></p><figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">react-native <span class="built_in">start</span> <span class="comment">--reset-cache</span></div></pre></td></tr></table></figure><p>最终原因是因为一个组件没删干净🤣</p><h3 id="问题九"><a href="#问题九" class="headerlink" title="问题九"></a>问题九</h3><p>出现:CFBundleIdentifier”, Does Not Exist 错误</p><p><strong>解决方法</strong><br>打开 xcode 运行项目出现<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-050038.png" alt=""><br>在这里面有个 libInteractable.a 删除掉就行了<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-050103.png" alt=""></p><p>参考:<a href="https://github.com/rebeccahughes/react-native-device-info/issues/251" target="_blank" rel="external">rebeccahughes/react-native-device-info#251</a></p><h3 id="问题十"><a href="#问题十" class="headerlink" title="问题十"></a>问题十</h3><p>删除包注意事项</p><p>首先 <code>react-native unlink <lib name></code><br>然后 <code>yarn remove <lib name></code></p><p><strong>一定要这样做</strong>不然会有问题。。。</p><h3 id="问题十一"><a href="#问题十一" class="headerlink" title="问题十一"></a>问题十一</h3><p>出现Invariant Violation: View config not found for name 红色警报<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-050336.png" alt=""></p><p><strong>解决方法</strong><br><a href="https://stackoverflow.com/questions/46750477/react-native-invariant-violation-view-config" target="_blank" rel="external">https://stackoverflow.com/questions/46750477/react-native-invariant-violation-view-config</a></p><h3 id="问题十二"><a href="#问题十二" class="headerlink" title="问题十二"></a>问题十二</h3><p>Button 组件无法直接使用 style定宽度和高度等等</p><p><strong>解决方法</strong></p><blockquote><p>If this button doesn’t look right for your app, you can build your own button using TouchableOpacity or TouchableNativeFeedback.</p></blockquote><p>也就是说可以使用 <code>TouchableOpacity</code> 或者 <code>TouchableNativeFeedback</code> 组件代替</p><h3 id="问题十三"><a href="#问题十三" class="headerlink" title="问题十三"></a>问题十三</h3><p>使用TouchableWithoutFeedback 出现错误<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-050857.png" alt=""></p><p><strong>解决方法</strong><br>TouchableWithoutFeedback,这个组件必须至少有一个child,如果是多个组件,必须以view来包装。写成这样就可以了</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">render() {</div><div class="line"> <span class="keyword">return</span>(</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">TouchableWithoutFeedback</span> <span class="attr">style</span>=<span class="string">{{flex:</span> <span class="attr">1</span>}} <span class="attr">onPress</span>=<span class="string">{dismissKeyboard}</span>></span></span></div><div class="line"> <span class="tag"><<span class="name">View</span> <span class="attr">style</span>=<span class="string">{{flex:</span> <span class="attr">1</span>}}></span></div><div class="line"> 。。。。。。。。。。</div><div class="line"> <span class="tag"></<span class="name">View</span>></span></div><div class="line"></div><div class="line"> <span class="tag"></<span class="name">TouchableWithoutFeedback</span>></span></div><div class="line"> );</div><div class="line">}</div></pre></td></tr></table></figure><h3 id="问题十四"><a href="#问题十四" class="headerlink" title="问题十四"></a>问题十四</h3><p>xcode出现Showing All Messages Code signing is required for product type ‘Unit Test Bundle’ in SDK ‘iOS 11.2’<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-051045.png" alt=""></p><p>在Xcode上<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-051232.png" alt=""><br>即可</p><h3 id="问题十五"><a href="#问题十五" class="headerlink" title="问题十五"></a>问题十五</h3><p>react-navigation TabNavigator点击切换反应迟钝。</p><p>在真机上调试react-navigation的TabNavigator,点击tab总感觉反应很慢,试了好久都是这样,大概有0.5秒之后才会切换体验很差。</p><p><strong>解决方法</strong><br>关闭debug模式。。。。</p><h3 id="问题十六"><a href="#问题十六" class="headerlink" title="问题十六"></a>问题十六</h3><p>React Navigation TabNavigator 一个帧的延迟</p><p>当页面加载时,下面的 tab 图标从第一个到第二个图标有一个帧的延迟</p><p><strong>解决方法</strong><br>定义initialLayout,用以防止react-native-tab-view渲染中一个帧的延迟</p><p>参考:<a href="https://github.com/react-native-community/react-native-tab-view#avoid-one-frame-delay" target="_blank" rel="external">https://github.com/react-native-community/react-native-tab-view#avoid-one-frame-delay</a></p><h3 id="问题十七"><a href="#问题十七" class="headerlink" title="问题十七"></a>问题十七</h3><p>出现timed out waiting for 红色警报<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-051715.png" alt=""></p><p><strong>解决方法</strong><br>重启模拟器。。。</p><h3 id="问题十八"><a href="#问题十八" class="headerlink" title="问题十八"></a>问题十八</h3><p>react native 没有\<br>组件换行</p><p><strong>解决方法</strong><br>可以在 Text 组件里写 {‘\n’},如:<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-051845.png" alt=""></p><h3 id="问题十九"><a href="#问题十九" class="headerlink" title="问题十九"></a>问题十九</h3><p>react native checkbox 原生组件只适合安卓</p><p><strong>解决方法</strong><br>可以使用这个 <a href="https://github.com/crazycodeboy/react-native-check-box" target="_blank" rel="external">https://github.com/crazycodeboy/react-native-check-box</a></p><h3 id="问题二十"><a href="#问题二十" class="headerlink" title="问题二十"></a>问题二十</h3><p>react-navigation的headerRight添加点击事件</p><p><strong>解决方法</strong><br>首先需要在componentDidMount(){}中动态的添加点击事件</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="title">componentDidMount</span><span class="params">()</span></span>{</div><div class="line"> this<span class="selector-class">.props</span><span class="selector-class">.navigation</span><span class="selector-class">.setParams</span>({</div><div class="line"> navigatePress:this<span class="selector-class">.navigatePress</span></div><div class="line"> })</div><div class="line">}</div></pre></td></tr></table></figure><p>然后</p><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="title">navigatePress</span> = <span class="params">()</span> =></span> {</div><div class="line"> alert(<span class="string">'点击headerRight'</span>);</div><div class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.props.navigation);</div><div class="line">}</div></pre></td></tr></table></figure><p>接下来就可以通过params方法来获取点击事件了(<strong>记住先要判断navigation.state.params是否存在,不然会报错</strong>。。。)</p><figure class="highlight pf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">static navigationOptions = ({ navigation, screenProps }) => ({</div><div class="line"> title: navigation.<span class="keyword">state</span>.params?navigation.<span class="keyword">state</span>.params.title:null,</div><div class="line"> headerRight:(</div><div class="line"> <span class="variable"><Text onPress={navigation.state.params?navigation.state.params.navigatePress:null}></span></div><div class="line"> 返回</div><div class="line"> </Text></div><div class="line"> )</div><div class="line">});</div></pre></td></tr></table></figure><h3 id="问题二十一"><a href="#问题二十一" class="headerlink" title="问题二十一"></a>问题二十一</h3><p>react-navigation tab 点击 StatusBar 颜色问题</p><p><strong>解决方法</strong><br>详情 <a href="https://reactnavigation.org/docs/status-bar.html" target="_blank" rel="external">https://reactnavigation.org/docs/status-bar.html</a></p><h3 id="问题二十二"><a href="#问题二十二" class="headerlink" title="问题二十二"></a>问题二十二</h3><p>Image 标签不支持 http 问题</p><p><strong>解决方法</strong><br>ios 9 以上,默认是Https请求,如需支持Http,修改info.plist文件添加键值对就好了<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-052245.png" alt=""></p><h3 id="问题二十三"><a href="#问题二十三" class="headerlink" title="问题二十三"></a>问题二十三</h3><p>react-native-swiper 动态数据渲染,翻页出现错乱</p><p><strong>出现</strong><br>一开始,使用静态的数据没问题,但是使用动态加载数据就出现问题,经过一些调试发现,可能是 index 的问题,在 github 库里搜 issue 果然有人遇到过这个问题 <a href="https://github.com/leecade/react-native-swiper/issues/720" target="_blank" rel="external">https://github.com/leecade/react-native-swiper/issues/720</a></p><p><strong>解决方法</strong><br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-052442.png" alt=""><br>添加 key 值是你<strong>获取数据的长度</strong></p><h3 id="问题二十四"><a href="#问题二十四" class="headerlink" title="问题二十四"></a>问题二十四</h3><p>react-native-swiper 跳转索引 bug 问题。</p><p>一开始<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-052540.png" alt=""></p><p>在翻页的时候,出现索引随机变化的问题,当时看了一下 api 是没有问题的,一直定位到<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-052704.png" alt=""></p><p>将标题不显示就发现索引没有问题了,不会随机翻页。。。,接着在某一页看到题目比较大,发生了抖动,结果造成了随机翻页,再一看里面有数字和文字,大小不一样,设置一下字体就好了。。。。个人认为是抖动的时候可能触发了react-native-swiper的翻页,结果造成随机翻页。。。<br>神坑的 bug,找了4,5个小时。。。。😡</p><h3 id="问题二十五"><a href="#问题二十五" class="headerlink" title="问题二十五"></a>问题二十五</h3><p>ListView, FlatList, Sections and VirtualizedList paddingBottom 无效的问题。</p><p>ListView, FlatList, Sections and VirtualizedList 继承了 ScrollView<br>所以导致都有这个问题</p><p><strong>解决方法</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">ScrollView</span> <span class="attr">contentContainerStyle</span>=<span class="string">{{paddingBottom:</span> <span class="attr">16</span>}} /></span></div></pre></td></tr></table></figure><h3 id="问题二十六"><a href="#问题二十六" class="headerlink" title="问题二十六"></a>问题二十六</h3><p>出现 could not connect to development server红色警报<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-05-08-052952.png" alt=""></p><p><strong>解决方法</strong><br>关掉 vpn ,或者不要开全局模式。。。很神奇</p>]]></content>
<summary type="html">
<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>前些天使用react-native 写了个项目,遇到的问题挺多的,在这里记录下来📝,避免忘记。本篇文章会不定期更新!</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="react" scheme="http://www.shenzekun.cn/tags/react/"/>
</entry>
<entry>
<title>html5调用摄像头功能</title>
<link href="http://www.shenzekun.cn/html5%E8%B0%83%E7%94%A8%E6%91%84%E5%83%8F%E5%A4%B4%E5%8A%9F%E8%83%BD.html"/>
<id>http://www.shenzekun.cn/html5调用摄像头功能.html</id>
<published>2018-05-05T13:19:14.000Z</published>
<updated>2018-10-21T12:51:44.850Z</updated>
<content type="html"><![CDATA[<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>前些天,线上笔试的时候,发现需要浏览器同意开启摄像头,感觉像是 js 调用的,由于当时笔试,也就没想到这么多🤣。今天闲来无事,看了下自己的 todo,发现有这个调用摄像头的todo,才想到😂。网上查了一下,果然 js 有调用摄像头的 api,为此自己写一个 demo ,避免忘记。</p></blockquote><a id="more"></a><h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><h3 id="调用摄像头"><a href="#调用摄像头" class="headerlink" title="调用摄像头"></a>调用摄像头</h3><p>一共有两种实现方式,一种是使用<code>navigator.getUserMedia</code>(<strong>该特性已经从 Web 标准中删除</strong>,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性),<strong>前面一种已经从 Web 标准中删除</strong>,仅为了向后兼容而存在,第二种是使用<code>navigator.mediaDevices.getUserMedia</code>(<strong>推荐使用</strong>),这两种方法 Safari 貌似都不支持。。。。</p><ul><li>第一种方法<code>navigator.getUserMedia</code>用法详见 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/getUserMedia" target="_blank" rel="external">mdn</a> ,代码如下:</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!doctype html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>摄像头调用1<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">video</span> <span class="attr">id</span>=<span class="string">"v"</span>></span><span class="tag"></<span class="name">video</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"> !(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">userMedia</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> navigator.getUserMedia = navigator.getUserMedia ||</div><div class="line"> navigator.webkitGetUserMedia ||</div><div class="line"> navigator.mozGetUserMedia ||</div><div class="line"> navigator.msGetUserMedia || <span class="literal">null</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (userMedia()) {</div><div class="line"> <span class="keyword">var</span> constraints = {</div><div class="line"> <span class="attr">video</span>: <span class="literal">true</span>,</div><div class="line"> <span class="attr">audio</span>: <span class="literal">false</span></div><div class="line"> };</div><div class="line"> <span class="keyword">var</span> media = navigator.getUserMedia(constraints, <span class="function"><span class="keyword">function</span> (<span class="params">stream</span>) </span>{</div><div class="line"> <span class="keyword">var</span> v = <span class="built_in">document</span>.getElementById(<span class="string">'v'</span>);</div><div class="line"> <span class="keyword">var</span> url = <span class="built_in">window</span>.URL || <span class="built_in">window</span>.webkitURL;</div><div class="line"> v.src = url ? url.createObjectURL(stream) : stream;</div><div class="line"> v.play();</div><div class="line"> }, <span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"ERROR"</span>);</div><div class="line"> <span class="built_in">console</span>.log(error);</div><div class="line"> });</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"不支持"</span>);</div><div class="line"> }</div><div class="line"> })();</div><div class="line"> <span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure><ul><li>第二种方法<code>navigator.mediaDevices.getUserMedia</code>用法详见<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia" target="_blank" rel="external">mdn</a>。<code>navigator.mediaDevices.getUserMedia</code> 其实和第一种差不多,主要第二种返回是一个 Promise 对象,代码如下:</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!doctype html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>摄像头调用2<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">video</span> <span class="attr">id</span>=<span class="string">"v"</span>></span><span class="tag"></<span class="name">video</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"> !(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象</span></div><div class="line"> <span class="keyword">if</span> (navigator.mediaDevices === <span class="literal">undefined</span>) {</div><div class="line"> navigator.mediaDevices = {};</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (navigator.mediaDevices.getUserMedia === <span class="literal">undefined</span>) {</div><div class="line"> navigator.mediaDevices.getUserMedia = <span class="function"><span class="keyword">function</span> (<span class="params">constraints</span>) </span>{</div><div class="line"> <span class="comment">// 首先,如果有getUserMedia的话,就获得它</span></div><div class="line"> <span class="keyword">var</span> getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;</div><div class="line"></div><div class="line"> <span class="comment">// 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口</span></div><div class="line"> <span class="keyword">if</span> (!getUserMedia) {</div><div class="line"> <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'getUserMedia is not implemented in this browser'</span>));</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// 否则,为老的navigator.getUserMedia方法包裹一个Promise</span></div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>{</div><div class="line"> getUserMedia.call(navigator, constraints, resolve, reject);</div><div class="line"> });</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> constraints = {</div><div class="line"> <span class="attr">video</span>: <span class="literal">true</span>,</div><div class="line"> <span class="attr">audio</span>: <span class="literal">false</span></div><div class="line"> };</div><div class="line"> <span class="keyword">let</span> promise = navigator.mediaDevices.getUserMedia(constraints);</div><div class="line"> promise.then(<span class="function"><span class="params">stream</span> =></span> {</div><div class="line"> <span class="keyword">let</span> v = <span class="built_in">document</span>.getElementById(<span class="string">'v'</span>);</div><div class="line"> <span class="comment">// 旧的浏览器可能没有srcObject</span></div><div class="line"> <span class="keyword">if</span> (<span class="string">"srcObject"</span> <span class="keyword">in</span> v) {</div><div class="line"> v.srcObject = stream;</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">// 防止再新的浏览器里使用它,应为它已经不再支持了</span></div><div class="line"> v.src = <span class="built_in">window</span>.URL.createObjectURL(stream);</div><div class="line"> }</div><div class="line"> v.onloadedmetadata = <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</div><div class="line"> v.play();</div><div class="line"> };</div><div class="line"> }).catch(<span class="function"><span class="params">err</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.error(err.name + <span class="string">": "</span> + err.message);</div><div class="line"> })</div><div class="line"> })();</div><div class="line"> <span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure><h3 id="拍照"><a href="#拍照" class="headerlink" title="拍照"></a>拍照</h3><p>思路是设置一个标志变量 videoPlaying 看看是否 video 有在 play,监听拍照按钮的点击事件,如果videoPlaying 为 true ,使用一个canvas 获取 video 的宽高(默认 canvas 是不显示的),然后使用 canvas 的<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage" target="_blank" rel="external">drawImage</a>,然后使用 canvas 的 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL" target="_blank" rel="external">toDataURL</a>返回一个 data url,将这个 url,设置在一个 img 标签上即可😀</p><ul><li>第一种方法<code>navigator.getUserMedia</code>实现代码:</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!doctype html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>拍照1<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">id</span>=<span class="string">"take"</span>></span>拍照<span class="tag"></<span class="name">button</span>></span></div><div class="line"> <span class="tag"><<span class="name">br</span> /></span></div><div class="line"> <span class="tag"><<span class="name">video</span> <span class="attr">id</span>=<span class="string">"v"</span> <span class="attr">style</span>=<span class="string">"width: 640px;height: 480px;"</span>></span><span class="tag"></<span class="name">video</span>></span></div><div class="line"> <span class="tag"><<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"canvas"</span> <span class="attr">style</span>=<span class="string">"display:none;"</span>></span><span class="tag"></<span class="name">canvas</span>></span></div><div class="line"> <span class="tag"><<span class="name">br</span> /></span></div><div class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"http://placehold.it/640&text=Your%20image%20here%20..."</span> <span class="attr">id</span>=<span class="string">"photo"</span> <span class="attr">alt</span>=<span class="string">"photo"</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"> !(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">userMedia</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> navigator.getUserMedia = navigator.getUserMedia ||</div><div class="line"> navigator.webkitGetUserMedia ||</div><div class="line"> navigator.mozGetUserMedia ||</div><div class="line"> navigator.msGetUserMedia || <span class="literal">null</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (userMedia()) {</div><div class="line"> <span class="keyword">let</span> videoPlaying = <span class="literal">false</span>;</div><div class="line"> <span class="keyword">let</span> constraints = {</div><div class="line"> <span class="attr">video</span>: <span class="literal">true</span>,</div><div class="line"> <span class="attr">audio</span>: <span class="literal">false</span></div><div class="line"> };</div><div class="line"> <span class="keyword">let</span> video = <span class="built_in">document</span>.getElementById(<span class="string">'v'</span>);</div><div class="line"> <span class="keyword">let</span> media = navigator.getUserMedia(constraints, <span class="function"><span class="keyword">function</span> (<span class="params">stream</span>) </span>{</div><div class="line"> <span class="keyword">let</span> url = <span class="built_in">window</span>.URL || <span class="built_in">window</span>.webkitURL;</div><div class="line"> video.src = url ? url.createObjectURL(stream) : stream;</div><div class="line"> video.play();</div><div class="line"> videoPlaying = <span class="literal">true</span>;</div><div class="line"> }, <span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"ERROR"</span>);</div><div class="line"> <span class="built_in">console</span>.log(error);</div><div class="line"> });</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'take'</span>).addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">if</span> (videoPlaying) {</div><div class="line"> <span class="keyword">let</span> canvas = <span class="built_in">document</span>.getElementById(<span class="string">'canvas'</span>);</div><div class="line"> canvas.width = video.videoWidth;</div><div class="line"> canvas.height = video.videoHeight;</div><div class="line"> canvas.getContext(<span class="string">'2d'</span>).drawImage(video, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line"> <span class="keyword">let</span> data = canvas.toDataURL(<span class="string">'image/webp'</span>);</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'photo'</span>).setAttribute(<span class="string">'src'</span>, data);</div><div class="line"> }</div><div class="line"> }, <span class="literal">false</span>);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"不支持"</span>);</div><div class="line"> }</div><div class="line"> })();</div><div class="line"> <span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure><p>第二种<code>navigator.mediaDevices.getUserMedia</code>实现方法:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div></pre></td><td class="code"><pre><div class="line"><span class="meta"><!doctype html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>拍照2<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">id</span>=<span class="string">"take"</span>></span>拍照<span class="tag"></<span class="name">button</span>></span></div><div class="line"> <span class="tag"><<span class="name">br</span> /></span></div><div class="line"> <span class="tag"><<span class="name">video</span> <span class="attr">id</span>=<span class="string">"v"</span> <span class="attr">style</span>=<span class="string">"width: 640px;height: 480px;"</span>></span><span class="tag"></<span class="name">video</span>></span></div><div class="line"> <span class="tag"><<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"canvas"</span> <span class="attr">style</span>=<span class="string">"display:none;"</span>></span><span class="tag"></<span class="name">canvas</span>></span></div><div class="line"> <span class="tag"><<span class="name">br</span> /></span></div><div class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"http://placehold.it/640&text=Your%20image%20here%20..."</span> <span class="attr">id</span>=<span class="string">"photo"</span> <span class="attr">alt</span>=<span class="string">"photo"</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span>></span><span class="javascript"></span></div><div class="line"> !(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="comment">// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象</span></div><div class="line"> <span class="keyword">if</span> (navigator.mediaDevices === <span class="literal">undefined</span>) {</div><div class="line"> navigator.mediaDevices = {};</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (navigator.mediaDevices.getUserMedia === <span class="literal">undefined</span>) {</div><div class="line"> navigator.mediaDevices.getUserMedia = <span class="function"><span class="keyword">function</span> (<span class="params">constraints</span>) </span>{</div><div class="line"> <span class="comment">// 首先,如果有getUserMedia的话,就获得它</span></div><div class="line"> <span class="keyword">var</span> getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;</div><div class="line"></div><div class="line"> <span class="comment">// 一些浏览器根本没实现它 - 那么就返回一个error到promise的reject来保持一个统一的接口</span></div><div class="line"> <span class="keyword">if</span> (!getUserMedia) {</div><div class="line"> <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'getUserMedia is not implemented in this browser'</span>));</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// 否则,为老的navigator.getUserMedia方法包裹一个Promise</span></div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span> (<span class="params">resolve, reject</span>) </span>{</div><div class="line"> getUserMedia.call(navigator, constraints, resolve, reject);</div><div class="line"> });</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">const</span> constraints = {</div><div class="line"> <span class="attr">video</span>: <span class="literal">true</span>,</div><div class="line"> <span class="attr">audio</span>: <span class="literal">false</span></div><div class="line"> };</div><div class="line"> <span class="keyword">let</span> videoPlaying = <span class="literal">false</span>;</div><div class="line"> <span class="keyword">let</span> v = <span class="built_in">document</span>.getElementById(<span class="string">'v'</span>);</div><div class="line"> <span class="keyword">let</span> promise = navigator.mediaDevices.getUserMedia(constraints);</div><div class="line"> promise.then(<span class="function"><span class="params">stream</span> =></span> {</div><div class="line"> <span class="comment">// 旧的浏览器可能没有srcObject</span></div><div class="line"> <span class="keyword">if</span> (<span class="string">"srcObject"</span> <span class="keyword">in</span> v) {</div><div class="line"> v.srcObject = stream;</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">// 防止再新的浏览器里使用它,应为它已经不再支持了</span></div><div class="line"> v.src = <span class="built_in">window</span>.URL.createObjectURL(stream);</div><div class="line"> }</div><div class="line"> v.onloadedmetadata = <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</div><div class="line"> v.play();</div><div class="line"> videoPlaying = <span class="literal">true</span>;</div><div class="line"> };</div><div class="line"> }).catch(<span class="function"><span class="params">err</span> =></span> {</div><div class="line"> <span class="built_in">console</span>.error(err.name + <span class="string">": "</span> + err.message);</div><div class="line"> })</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'take'</span>).addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">if</span> (videoPlaying) {</div><div class="line"> <span class="keyword">let</span> canvas = <span class="built_in">document</span>.getElementById(<span class="string">'canvas'</span>);</div><div class="line"> canvas.width = v.videoWidth;</div><div class="line"> canvas.height = v.videoHeight;</div><div class="line"> canvas.getContext(<span class="string">'2d'</span>).drawImage(v, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line"> <span class="keyword">let</span> data = canvas.toDataURL(<span class="string">'image/webp'</span>);</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'photo'</span>).setAttribute(<span class="string">'src'</span>, data);</div><div class="line"> }</div><div class="line"> }, <span class="literal">false</span>);</div><div class="line"> })();</div><div class="line"> <span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>前些天,线上笔试的时候,发现需要浏览器同意开启摄像头,感觉像是 js 调用的,由于当时笔试,也就没想到这么多🤣。今天闲来无事,看了下自己的 todo,发现有这个调用摄像头的todo,才想到😂。网上查了一下,果然 js 有调用摄像头的 api,为此自己写一个 demo ,避免忘记。</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="js" scheme="http://www.shenzekun.cn/tags/js/"/>
<category term="前端" scheme="http://www.shenzekun.cn/tags/%E5%89%8D%E7%AB%AF/"/>
<category term="html" scheme="http://www.shenzekun.cn/tags/html/"/>
</entry>
<entry>
<title>2018春招面试总结</title>
<link href="http://www.shenzekun.cn/2018%E6%98%A5%E6%8B%9B%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93.html"/>
<id>http://www.shenzekun.cn/2018春招面试总结.html</id>
<published>2018-04-20T03:38:42.000Z</published>
<updated>2018-10-21T12:51:44.846Z</updated>
<content type="html"><![CDATA[<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>从3月底到4月中旬,经过半个月的面试,终于在4月17号顺利拿到了阿里的 offer,感谢这段时间内面试官、家人和朋友的帮助和鼓励。下面我会总结一些企业关注的点和面试的一些技巧。</p></blockquote><a id="more"></a><h2 id="企业关注的点"><a href="#企业关注的点" class="headerlink" title="企业关注的点"></a>企业关注的点</h2><p>我认为大企业招聘技术实习生或者是应届生时关注有以下几点:</p><ul><li>你的学历(很重要,我腾讯现场二面估计就是挂在这上面,一上来就问我学校的排名😂)</li><li>你的成绩排名</li><li>你的项目经验</li><li>你的比赛奖项(关注会比较少,除非是获得 acm 那种大型比赛)</li><li>对技术是否热情</li><li>你的计算机基础</li><li>你的价值观</li><li>你的性格</li><li>你对当前应聘岗位的知识储备</li><li>沟通能力和团队协作</li><li>是否能够主动去学习新的东西</li></ul><p>在面试时,这些方面都会有所体现。<br>比如问你一些常规性问题(考察你对当前应聘岗位的知识储备)。<br>问你一些计算机基础问题(考察你的计算机基础)。<br>HR 问你一些关于性格优缺点和一些其他问题(考察你的性格和你的价值观)。<br>问你一些场景题(考察你的项目经验)。<br>问你最近看什么书或者你常逛什么技术社区(考察你是否能够主动去学习新的东西和你对技术是否有热情)等等。</p><h2 id="面试技巧"><a href="#面试技巧" class="headerlink" title="面试技巧"></a>面试技巧</h2><p>首先,在面试中,<strong>一定要自信不要紧张</strong>。遇到不会的问题不要慌张,可以将不会的问题转化为自己会的问题。我举个例子,比如面试官问你前端的<code>async</code> 和 <code>await</code> ,那么此时如果你这个不会,那么你可以问一下面试官这个主要是干嘛的,面试官一般都会跟你解释,他告诉你这是异步,那么你就可以往异步的方面靠,这样会让面试官觉得你掌握的还行🙂。</p><p>其次,就是<strong>简历的项目经验和技能一定要写好</strong>,因为面试官会一直问项目的实现,难点,和项目产生的背景。然后会问你在简历上的一些相关技能。个人认为如果不太会的技能就不要写上去。比如我写了<strong>了解</strong>前端安全和性能优化,然后很多面试官就问这个。。。一直问。。。还好准备的比较充分,不然就 gg 了🤣</p><p>最后,在面试时,<strong>千万不要抱怨你的队友,即便队友很水</strong>。我举个反例,有人在 hr 面试时,hr 问他对队友看法,然后他说队友很水,整个项目都是他在写,然后当天就挂了。你想,企业会要背后说别人闲话的人吗?</p><p>最后的最后,面试一定要保持<strong>积极的心态</strong>,就当面试是在自我学习的过程,每次面试都自我反省一下,到底哪些问题回答不好,然后去查缺补漏,这样一定会收获到一个自己满意的 offer 😀</p>]]></content>
<summary type="html">
<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>从3月底到4月中旬,经过半个月的面试,终于在4月17号顺利拿到了阿里的 offer,感谢这段时间内面试官、家人和朋友的帮助和鼓励。下面我会总结一些企业关注的点和面试的一些技巧。</p></blockquote>
</summary>
<category term="随笔" scheme="http://www.shenzekun.cn/categories/%E9%9A%8F%E7%AC%94/"/>
<category term="面试" scheme="http://www.shenzekun.cn/tags/%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>vue全家桶与typescript使用总结</title>
<link href="http://www.shenzekun.cn/vue%E5%85%A8%E5%AE%B6%E6%A1%B6%E4%B8%8Etypescript%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93.html"/>
<id>http://www.shenzekun.cn/vue全家桶与typescript使用总结.html</id>
<published>2018-03-01T03:38:31.000Z</published>
<updated>2018-10-22T02:45:28.182Z</updated>
<content type="html"><![CDATA[<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>最近重构了我之前项目 qq 音乐移动端,使用的技术是 vue,vuex,vue-router,和 typescript,在这期间,遇到的问题还是蛮多的,一会儿我会把我遇到的问题以及解决方法列出来,避免忘记。</p></blockquote><a id="more"></a><p>重构完成的项目 ===> <a href="https://github.com/shenzekun/vue-qq-music" target="_blank" rel="external">vue-qq-music</a></p><p>TypeScript与Vue全家桶的配置可以参考以下两篇文章(在这里由衷感谢两位作者):</p><ol><li><p><a href="https://segmentfault.com/a/1190000011744210#articleHeader12" target="_blank" rel="external">vue + typescript 新项目起手式</a></p></li><li><p><a href="https://segmentfault.com/a/1190000011864013" target="_blank" rel="external">Vue2.5+ Typescript 引入全面指南 - Vuex篇</a></p></li></ol><h2 id="TypeScript"><a href="#TypeScript" class="headerlink" title="TypeScript"></a>TypeScript</h2><p>为什么我要将<code>TypeScript</code> 和 <code>Vue</code> 集成呢?因为TypeScript 有以下几个优势:</p><ul><li><strong>可读性</strong>。TypeScript 是 JavaScript 的超集,这意味着他支持所有的 JavaScript 语法。并在此之上对 JavaScript 添加了一些扩展,如interface等。这样会大大提升代码的可阅读性</li><li><strong>静态类型检查</strong>。静态类型检查可以避免很多不必要的错误,不用在调试的时候才发现问题。</li><li><strong>代码提示</strong>。ts 搭配 vscode,代码提示非常友好</li><li><strong>代码重构</strong>。例如全项目更改某个变量名(也可以是类名、方法名,甚至是文件名[重命名文件自动修改的是整个项目的import]),在JS中是不可能的,而TS可以轻松做到。看看下面发生了什么神奇的事情😁⬇️<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-03-01-ts-chonggou.gif" alt=""></li></ul><h2 id="遇到的问题以及解决方法"><a href="#遇到的问题以及解决方法" class="headerlink" title="遇到的问题以及解决方法"></a>遇到的问题以及解决方法</h2><h3 id="问题一"><a href="#问题一" class="headerlink" title="问题一"></a>问题一</h3><p>ts 无法识别$ref</p><p><strong>解决方法</strong><br>① 直接在 this.$refs.xxx 后面申明类型如:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">this</span>.$refs.lyricsLines <span class="keyword">as</span> HTMLDivElement;</div></pre></td></tr></table></figure><p>② 在<code>export default class xxx extends Vue</code>里面声明全部的$ref 的类型</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$refs: {</div><div class="line"> <span class="attr">audio</span>: HTMLAudioElement,</div><div class="line"> <span class="attr">lyricsLines</span>: HTMLDivElement</div><div class="line">}</div></pre></td></tr></table></figure><h3 id="问题二"><a href="#问题二" class="headerlink" title="问题二"></a>问题二</h3><p>ts 无法识别 require</p><p><strong>解决方法</strong></p><p>安装声明文件</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">yarn add @types/webpack-env -D</div></pre></td></tr></table></figure><h3 id="问题三"><a href="#问题三" class="headerlink" title="问题三"></a>问题三</h3><p>运行<code>npm run build</code> 出现<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-03-01-044916.png" alt=""></p><p><strong>解决方法</strong></p><blockquote><p>You can fix this by <strong>using the most recent beta version</strong> of <code>uglifyjs-webpack-plugin</code>. Our team is working to remove completely the UglifyJsPlugin from within webpack, and instead have it as a standalone plugin.</p><p>If you do <code>yarn add uglifyjs-webpack-plugin@beta --dev</code> or <code>npm install uglifyjs-webpack-plugin@beta --save-dev</code>you should receive the latest beta which does successfully minify es6 syntax. We are hoping to have this released from beta extremely soon, however it should save you from errors for now!</p></blockquote><p>也就是说升级你的uglifyjs-webpack-plugin版本:<br><code>yarn add uglifyjs-webpack-plugin@beta --dev</code></p><h3 id="问题四"><a href="#问题四" class="headerlink" title="问题四"></a>问题四</h3><p><a href="https://github.com/kaorun343/vue-property-decorator" target="_blank" rel="external">vue-property-decorator</a> 装饰器写法不对。当时我是要把 mixins,注入到组件里,我就这样写:<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-03-01-052833.png" alt=""><br>ts提示找不到 mixin。我就很纳闷为什么找不到名字,由于官网vue-property-decorator例子太少,只好一步一步摸索😂</p><p><strong>解决方法</strong></p><p>把mixins写在@Component里面…,像这样:</p><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2018-03-01-053215.png" alt=""></p><h2 id="注意点"><a href="#注意点" class="headerlink" title="注意点"></a>注意点</h2><ol><li>如果你引用第三方无类型声明的库,那就需要自己编写x.d.ts文件</li><li>如果引用 ui 组件的时候,如果控制台出现<code>Property '$xxx' does not exist on type 'App'</code>的话,那么可以在<code>vue-shim.d.ts</code>增加</li></ol><figure class="highlight typescript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">declare</span> <span class="keyword">module</span> 'vue/types/vue' {</div><div class="line"> <span class="keyword">interface</span> Vue {</div><div class="line"> $xxx: <span class="built_in">any</span>,</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>经过几天的折腾,终于把项目重构完成,我个人认为加上 <code>TypeScript</code>,确实效率挺高了许多,不过 <code>Vue+TypeScript</code> 还是没 <code>Angular</code>支持那么完善,相信之后 vue 对于 ts 的集成会更加完善!</p>]]></content>
<summary type="html">
<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>最近重构了我之前项目 qq 音乐移动端,使用的技术是 vue,vuex,vue-router,和 typescript,在这期间,遇到的问题还是蛮多的,一会儿我会把我遇到的问题以及解决方法列出来,避免忘记。</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="vue" scheme="http://www.shenzekun.cn/tags/vue/"/>
<category term="typescript" scheme="http://www.shenzekun.cn/tags/typescript/"/>
</entry>
<entry>
<title>node简单实现一个更改头像功能</title>
<link href="http://www.shenzekun.cn/node%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E6%9B%B4%E6%94%B9%E5%A4%B4%E5%83%8F%E5%8A%9F%E8%83%BD.html"/>
<id>http://www.shenzekun.cn/node简单实现一个更改头像功能.html</id>
<published>2017-12-28T02:23:05.000Z</published>
<updated>2018-10-22T02:44:10.652Z</updated>
<content type="html"><![CDATA[<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>一直想写这篇文章,无奈由于要考试的原因,一直在复习,拖延到现在才写🤣,之前用 node 的 express 框架写了个小项目,里面有个上传图片的功能,这里记录一下如何实现(我使用的是 <strong>ejs</strong>)📝</p></blockquote><a id="more"></a><h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><ol><li><p><strong>首先</strong>,当用户点击上传头像,更新头像的时候,将头像上传到项目的一个文件夹里面(<em>我是存放在项目的<code>public/images/img</code>里面</em>),并且将图像名重命名(<em>可以以时间戳来命名</em>)。<img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15144357069018.jpg" alt=""></p></li><li><p><strong>同时</strong>图片在项目的路径插入到用户表的当前用户的 <code>userpicturepath</code> 里面</p></li><li>然后更新用户的 session,将图片里面的路径赋值给 session 的里面的<code>picture</code>属性里面</li><li><code><img></code> 的 <code>src</code> 获取到当前用户的session里面的 <code>picture</code> 的值,最后动态刷新页面头像就换成了用户上传的头像了</li></ol><h2 id="实现效果"><a href="#实现效果" class="headerlink" title="实现效果"></a>实现效果</h2><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/2017-12-28-user-upload.gif" alt=""></p><h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><p>ejs部分</p><figure class="highlight vbscript-html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="xml"><span class="tag"><<span class="name">img</span> <span class="attr">class</span>=<span class="string">"nav-user-photo"</span> <span class="attr">src</span>=<span class="string">"</span></span></span><span class="vbscript"><%= user.picture.<span class="built_in">replace</span>(/<span class="keyword">public</span>(\/.*)/, <span class="string">"$1"</span>) %></span><span class="xml"><span class="tag"><span class="string">"</span> <span class="attr">alt</span>=<span class="string">"Photo"</span> <span class="attr">style</span>=<span class="string">"height: 40px;"</span>/></span></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">form</span> <span class="attr">enctype</span>=<span class="string">"multipart/form-data"</span> <span class="attr">method</span>=<span class="string">"post"</span> <span class="attr">name</span>=<span class="string">"fileInfo"</span>></span></div><div class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"file"</span> <span class="attr">accept</span>=<span class="string">"image/png,image/jpg"</span> <span class="attr">id</span>=<span class="string">"picUpload"</span> <span class="attr">name</span>=<span class="string">"file"</span>></span></div><div class="line"><span class="tag"></<span class="name">form</span>></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">button</span> <span class="attr">type</span>=<span class="string">"button"</span> <span class="attr">class</span>=<span class="string">"btn btn-primary"</span> <span class="attr">id</span>=<span class="string">"modifyPicV"</span>></span>确定<span class="tag"></<span class="name">button</span>></span></div></pre></td></tr></table></figure><p>js部分</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">document</span>.querySelector(<span class="string">'#modifyPicV'</span>).addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">let</span> formData = <span class="keyword">new</span> FormData();</div><div class="line"> formData.append(<span class="string">"file"</span>,$(<span class="string">"input[name='file']"</span>)[<span class="number">0</span>].files[<span class="number">0</span>]);<span class="comment">//把文件对象插到formData对象上</span></div><div class="line"> <span class="built_in">console</span>.log(formData.get(<span class="string">'file'</span>));</div><div class="line"> $.ajax({</div><div class="line"> <span class="attr">url</span>:<span class="string">'/modifyPic'</span>,</div><div class="line"> <span class="attr">type</span>:<span class="string">'post'</span>,</div><div class="line"> <span class="attr">data</span>: formData,</div><div class="line"> <span class="attr">processData</span>: <span class="literal">false</span>, <span class="comment">// 不处理数据</span></div><div class="line"> contentType: <span class="literal">false</span>, <span class="comment">// 不设置内容类型</span></div><div class="line"> success:<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> alert(<span class="string">'success'</span>);</div><div class="line"> location.reload();</div><div class="line"> },</div><div class="line"> })</div><div class="line">});</div></pre></td></tr></table></figure><p>路由部分,使用<code>formidable</code>,这是一个Node.js模块,用于解析表单数据,尤其是文件上传</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">let</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</div><div class="line"><span class="keyword">let</span> router = express.Router();</div><div class="line"><span class="keyword">let</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</div><div class="line"><span class="keyword">let</span> {User} = <span class="built_in">require</span>(<span class="string">'../data/db'</span>);</div><div class="line"><span class="keyword">let</span> formidable = <span class="built_in">require</span>(<span class="string">'formidable'</span>);</div><div class="line"><span class="keyword">let</span> cacheFolder = <span class="string">'public/images/'</span>;<span class="comment">//放置路径</span></div><div class="line">router.post(<span class="string">'/modifyPic'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res, next</span>) </span>{</div><div class="line"> <span class="keyword">let</span> userDirPath = cacheFolder + <span class="string">"Img"</span>;</div><div class="line"> <span class="keyword">if</span> (!fs.existsSync(userDirPath)) {</div><div class="line"> fs.mkdirSync(userDirPath);<span class="comment">//创建目录</span></div><div class="line"> }</div><div class="line"> <span class="keyword">let</span> form = <span class="keyword">new</span> formidable.IncomingForm(); <span class="comment">//创建上传表单</span></div><div class="line"> form.encoding = <span class="string">'utf-8'</span>; <span class="comment">//设置编码</span></div><div class="line"> form.uploadDir = userDirPath; <span class="comment">//设置上传目录</span></div><div class="line"> form.keepExtensions = <span class="literal">true</span>; <span class="comment">//保留后缀</span></div><div class="line"> form.maxFieldsSize = <span class="number">2</span> * <span class="number">1024</span> * <span class="number">1024</span>; <span class="comment">//文件大小</span></div><div class="line"> form.type = <span class="literal">true</span>;</div><div class="line"> form.parse(req, <span class="function"><span class="keyword">function</span> (<span class="params">err, fields, files</span>) </span>{</div><div class="line"> <span class="keyword">if</span> (err) {</div><div class="line"> <span class="keyword">return</span> res.json(err);</div><div class="line"> }</div><div class="line"> <span class="keyword">let</span> extName = <span class="string">''</span>; <span class="comment">//后缀名</span></div><div class="line"> <span class="keyword">switch</span> (files.file.type) {</div><div class="line"> <span class="keyword">case</span> <span class="string">'image/pjpeg'</span>:</div><div class="line"> extName = <span class="string">'jpg'</span>;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="keyword">case</span> <span class="string">'image/jpeg'</span>:</div><div class="line"> extName = <span class="string">'jpg'</span>;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="keyword">case</span> <span class="string">'image/png'</span>:</div><div class="line"> extName = <span class="string">'png'</span>;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="keyword">case</span> <span class="string">'image/x-png'</span>:</div><div class="line"> extName = <span class="string">'png'</span>;</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (extName.length === <span class="number">0</span>) {</div><div class="line"> <span class="keyword">return</span> res.json({</div><div class="line"> <span class="attr">msg</span>: <span class="string">'只支持png和jpg格式图片'</span></div><div class="line"> });</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">let</span> avatarName = <span class="string">'/'</span> + <span class="built_in">Date</span>.now() + <span class="string">'.'</span> + extName;</div><div class="line"> <span class="keyword">let</span> newPath = form.uploadDir + avatarName;</div><div class="line"> fs.renameSync(files.file.path, newPath); <span class="comment">//重命名</span></div><div class="line"> <span class="built_in">console</span>.log(newPath)</div><div class="line"> <span class="comment">//更新表</span></div><div class="line"> User.update({</div><div class="line"> <span class="attr">picture</span>: newPath</div><div class="line"> }, {</div><div class="line"> <span class="attr">where</span>: {</div><div class="line"> <span class="attr">username</span>: req.session.user.username</div><div class="line"> }</div><div class="line"> }).then(<span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</div><div class="line"> <span class="keyword">if</span> (data[<span class="number">0</span>] !== <span class="literal">undefined</span>) {</div><div class="line"> User.findAll({</div><div class="line"> <span class="attr">where</span>: {</div><div class="line"> <span class="attr">username</span>: req.session.user.username</div><div class="line"> }</div><div class="line"> }).then(<span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</div><div class="line"> <span class="keyword">if</span> (data[<span class="number">0</span>] !== <span class="literal">undefined</span>) {</div><div class="line"> req.session.user.picture = data[<span class="number">0</span>].dataValues.picture;</div><div class="line"> res.send(<span class="literal">true</span>);</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> res.send(<span class="literal">false</span>);</div><div class="line"> }</div><div class="line"> })</div><div class="line"> }</div><div class="line"> }).catch(<span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(err);</div><div class="line"> });</div><div class="line"> }</div><div class="line"> });</div><div class="line">});</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>一直想写这篇文章,无奈由于要考试的原因,一直在复习,拖延到现在才写🤣,之前用 node 的 express 框架写了个小项目,里面有个上传图片的功能,这里记录一下如何实现(我使用的是 <strong>ejs</strong>)📝</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="node" scheme="http://www.shenzekun.cn/tags/node/"/>
</entry>
<entry>
<title>解决 webstrom 上的 babel 编译问题</title>
<link href="http://www.shenzekun.cn/%E8%A7%A3%E5%86%B3-webstrom-%E4%B8%8A%E7%9A%84-babel-%E7%BC%96%E8%AF%91%E9%97%AE%E9%A2%98.html"/>
<id>http://www.shenzekun.cn/解决-webstrom-上的-babel-编译问题.html</id>
<published>2017-12-04T23:13:15.000Z</published>
<updated>2018-10-21T12:51:44.859Z</updated>
<content type="html"><![CDATA[<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>近日,在写 ejs 文件时,我发现用 vscode 没有啥提示,因此换成 webStrom ,但是用 webStrom 将 es6 编译成 es5 的时候出现了一些问题😭,经过一番搜索, 最后终于成功解决,这里记录一下🖊</p></blockquote><a id="more"></a><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><ul><li><p>首先建立一个新的工程,点击<strong>设置</strong></p></li><li><p>在设置里面,把JavaScript language version改成<strong>ECMAScript 6</strong><br><img src="http://upload-images.jianshu.io/upload_images/5308475-384e088e614a106c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p></li><li><p>然后在js文件里写一段ES6代码</p></li><li><p>现在IDE会出现一个<code>File watcher</code>提示条</p></li><li><p>此时先别点Add watcher!</p></li><li><p>在终端切换到项目的路径,输入以下命令</p></li></ul><figure class="highlight swift"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm <span class="keyword">init</span> -y <span class="comment">//package.json</span></div></pre></td></tr></table></figure><ul><li>安装babel-cli</li></ul><figure class="highlight q"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install --<span class="built_in">save</span>-<span class="built_in">dev</span> babel-cli</div></pre></td></tr></table></figure><ul><li><p>现在可以去点Add watcher,点完之后会弹出一个框</p></li><li><p>下面第三行,<code>Program</code> 那一项,填</p></li></ul><figure class="highlight awk"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="variable">$ProjectFileDir</span>$<span class="regexp">/node_modules/</span>.bin<span class="regexp">/babel</span></div></pre></td></tr></table></figure><ul><li><p>然后点OK,这个时候你就会发现左边多出来一个新文件</p></li><li><p>但是现在还没搞定!现在只是搞定了自动转换的功能,系统默认把ES6 编译成了ES6…</p></li><li><p>打开终端,输入:</p></li></ul><figure class="highlight q"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install --<span class="built_in">save</span>-<span class="built_in">dev</span> babel-preset-es2015</div></pre></td></tr></table></figure><ul><li><p>再次打开设置,在搜索框输入<code>file watchers</code>,点击<code>babel</code></p></li><li><p>在 Arguments 里面将 <code>env</code> 改为 <code>=es2015</code>,点击ok<br><img src="http://upload-images.jianshu.io/upload_images/5308475-cd5fda1fb471aa6b.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p></li><li><p>在根目录下新建一个<code>.babelrc</code>文件(就是babel在当前项目的配置文件),写上:</p></li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> <span class="attr">"presets"</span>: [</div><div class="line"> <span class="string">"es2015"</span></div><div class="line"> ]</div><div class="line">}</div></pre></td></tr></table></figure><ul><li>完成😁</li></ul>]]></content>
<summary type="html">
<hr><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>近日,在写 ejs 文件时,我发现用 vscode 没有啥提示,因此换成 webStrom ,但是用 webStrom 将 es6 编译成 es5 的时候出现了一些问题😭,经过一番搜索, 最后终于成功解决,这里记录一下🖊</p></blockquote>
</summary>
<category term="技巧" scheme="http://www.shenzekun.cn/categories/%E6%8A%80%E5%B7%A7/"/>
<category term="工具" scheme="http://www.shenzekun.cn/tags/%E5%B7%A5%E5%85%B7/"/>
<category term="babel" scheme="http://www.shenzekun.cn/tags/babel/"/>
</entry>
<entry>
<title>解决安装electron卡在node install.js 不动问题</title>
<link href="http://www.shenzekun.cn/%E8%A7%A3%E5%86%B3%E5%AE%89%E8%A3%85electron%E5%8D%A1%E5%9C%A8node-install-js-%E4%B8%8D%E5%8A%A8%E9%97%AE%E9%A2%98.html"/>
<id>http://www.shenzekun.cn/解决安装electron卡在node-install-js-不动问题.html</id>
<published>2017-11-12T23:54:57.000Z</published>
<updated>2018-10-21T12:51:44.853Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>在安装electron的时候,一直卡在node install.js不动😭,翻了墙也不行,于是在 github 上搜索终于找到解决方法,为此记录下来📝</p></blockquote><a id="more"></a><h2 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h2><p><code>install.js</code>,里面的下载是依赖于<code>electron-download</code>这个模块<br><img src="http://upload-images.jianshu.io/upload_images/5308475-6a5758873a75a2c5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>搜索<a href="https://github.com/electron-userland/electron-download" target="_blank" rel="external">electron-download</a>发现:👇</p><p><img src="http://upload-images.jianshu.io/upload_images/5308475-0b73ee9483927679.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><h2 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h2><p>打开终端,输入<code>vi ~/.npmrc</code>,在里面添加</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="attr">electron_mirror</span>=<span class="string">"https://npm.taobao.org/mirrors/electron/"</span></div></pre></td></tr></table></figure><p><img src="http://upload-images.jianshu.io/upload_images/5308475-433a4c62d776c545.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>再试一次<code>npm install electron -g</code>,这次就成功了😄</p>]]></content>
<summary type="html">
<hr><blockquote><p>在安装electron的时候,一直卡在node install.js不动😭,翻了墙也不行,于是在 github 上搜索终于找到解决方法,为此记录下来📝</p></blockquote>
</summary>
<category term="技巧" scheme="http://www.shenzekun.cn/categories/%E6%8A%80%E5%B7%A7/"/>
<category term="技巧" scheme="http://www.shenzekun.cn/tags/%E6%8A%80%E5%B7%A7/"/>
</entry>
<entry>
<title>vscode 前端插件推荐</title>
<link href="http://www.shenzekun.cn/vscode-%E5%89%8D%E7%AB%AF%E6%8F%92%E4%BB%B6%E6%8E%A8%E8%8D%90.html"/>
<id>http://www.shenzekun.cn/vscode-前端插件推荐.html</id>
<published>2017-10-30T08:38:04.000Z</published>
<updated>2018-10-22T02:45:11.041Z</updated>
<content type="html"><![CDATA[<hr><a id="more"></a><h2 id="常用插件"><a href="#常用插件" class="headerlink" title="常用插件"></a>常用插件</h2><h3 id="Auto-Close-Tag"><a href="#Auto-Close-Tag" class="headerlink" title="Auto Close Tag"></a>Auto Close Tag</h3><blockquote><p>自动添加HTML / XML关闭标签(必备)</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15092830970499.gif" alt=""></p><h3 id="Auto-Rename-Tag"><a href="#Auto-Rename-Tag" class="headerlink" title="Auto Rename Tag"></a>Auto Rename Tag</h3><blockquote><p>自动重命名配对的HTML / XML标签(必备)</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15092838452118.gif" alt=""></p><h3 id="Beautify"><a href="#Beautify" class="headerlink" title="Beautify"></a>Beautify</h3><blockquote><p>格式化javascript,JSON,CSS,Sass,和HTML</p></blockquote><h3 id="Bootstrap-4-amp-Font-awesome-snippets"><a href="#Bootstrap-4-amp-Font-awesome-snippets" class="headerlink" title="Bootstrap 4 & Font awesome snippets"></a>Bootstrap 4 & Font awesome snippets</h3><blockquote><p>包含Bootstrap 4&Font awesome 的代码片段</p></blockquote><p><img src="https://ws1.sinaimg.cn/large/006tNc79ly1fkzg3v9bfnj30rk02ymx1.jpg" alt=""></p><h3 id="Bracket-Pair-Colorizer"><a href="#Bracket-Pair-Colorizer" class="headerlink" title="Bracket Pair Colorizer"></a>Bracket Pair Colorizer</h3><blockquote><p>颜色识别匹配括号</p></blockquote><p><img src="https://ws4.sinaimg.cn/large/006tNc79ly1fkzg8ththdj30k40500ss.jpg" alt=""></p><h3 id="Class-autocomplete-for-HTML"><a href="#Class-autocomplete-for-HTML" class="headerlink" title="Class autocomplete for HTML"></a>Class autocomplete for HTML</h3><blockquote><p>智能提示HTML class =“”属性(必备)</p></blockquote><h3 id="Code-Runner"><a href="#Code-Runner" class="headerlink" title="Code Runner"></a>Code Runner</h3><blockquote><p>非常强大的一款插件,能够运行多种语言的代码片段或代码文件:C,C ++,Java,JavaScript,PHP,Python,Perl,Ruby,Go等等,安装完成后,右上角出现:<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15092851845587.jpg" alt=""><br>点击这个按钮就可以运行你的文件了(必备)</p></blockquote><h3 id="css-peek"><a href="#css-peek" class="headerlink" title="css peek"></a>css peek</h3><blockquote><p>能够查看CSS ID和类的字符串作为HTML文件中相应的CSS定义(必备)</p></blockquote><p><strong>使用方法</strong>:将光标放在class里面的属性,右击<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15092856731870.jpg" alt=""></p><h3 id="Dash"><a href="#Dash" class="headerlink" title="Dash"></a>Dash</h3><blockquote><p>查文档必备,搭配 dash(不过似乎只有 mac 版)😁</p><p><strong>快捷键 <code>ctrl + h</code></strong> 它根据你当前选中的语言查找 dash 里面的文档</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/navicat-id-increase.gif" alt=""></p><h3 id="Debugger-for-Chrome"><a href="#Debugger-for-Chrome" class="headerlink" title="Debugger for Chrome"></a>Debugger for Chrome</h3><blockquote><p>让 vscode 映射 chrome 的 debug功能,使静态页面都可以用 vscode 来打断点调试</p></blockquote><p><strong>简单使用</strong>:<a href="https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001470969077294a6455fc9cd1f48b69f82cd05e7fa9b40000" target="_blank" rel="external">戳我</a></p><h3 id="Document-This"><a href="#Document-This" class="headerlink" title="Document This"></a>Document This</h3><blockquote><p>添加注释块</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093400730364.jpg" alt=""></p><p><strong>设置:</strong></p><figure class="highlight elixir"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="string">"docthis.includeAuthorTag"</span>: <span class="keyword">true</span>,<span class="regexp">//</span>出现<span class="variable">@Author</span></div><div class="line"><span class="string">"docthis.includeDescriptionTag"</span>: <span class="keyword">true</span>,<span class="regexp">//</span>出现<span class="variable">@Description</span></div><div class="line"><span class="string">"docthis.authorName"</span>: <span class="string">"shenzekun"</span>,<span class="regexp">//</span>作者名字</div></pre></td></tr></table></figure><p><strong>快捷键</strong>: 按两次Ctrl+alt+d</p><h3 id="ESLint"><a href="#ESLint" class="headerlink" title="ESLint"></a>ESLint</h3><blockquote><p>EsLint可以帮助我们检查Javascript编程时的语法错误。比如:在Javascript应用中,你很难找到你漏泄的变量或者方法。EsLint能够帮助我们分析JS代码,找到bug并确保一定程度的JS语法书写的正确性。</p></blockquote><p><strong>配置</strong>:<a href="https://segmentfault.com/a/1190000010462601" target="_blank" rel="external">戳我</a></p><h3 id="Font-awesome-codes-for-html"><a href="#Font-awesome-codes-for-html" class="headerlink" title="Font-awesome codes for html"></a>Font-awesome codes for html</h3><blockquote><p>用于 html 的Font-awesome代码片段</p></blockquote><h3 id="filesize"><a href="#filesize" class="headerlink" title="filesize"></a>filesize</h3><blockquote><p>在底部状态栏显示当前文件大小,点击后还可以看到详细创建、修改时间</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093414045557.jpg" alt=""></p><h3 id="Git-History"><a href="#Git-History" class="headerlink" title="Git History"></a>Git History</h3><blockquote><p>以图表的形式查看git日志<br><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093254060028.jpg" alt=""></p></blockquote><p>使用 command+shift+p(Ctrl+shift+p) 输入git log就可以看到了</p><h3 id="Git-Lens"><a href="#Git-Lens" class="headerlink" title="Git Lens"></a>Git Lens</h3><blockquote><p>git 日志插件</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093256313651.jpg" alt=""></p><h3 id="HTML-CSS-Support"><a href="#HTML-CSS-Support" class="headerlink" title="HTML CSS Support"></a>HTML CSS Support</h3><blockquote><p>在 html 标签上写class 智能提示当前项目所支持的样式(必备)</p></blockquote><h3 id="HTML-Snippets"><a href="#HTML-Snippets" class="headerlink" title="HTML Snippets"></a>HTML Snippets</h3><blockquote><p>html 代码片段(必备)</p></blockquote><h3 id="htmlhint"><a href="#htmlhint" class="headerlink" title="htmlhint"></a>htmlhint</h3><blockquote><p>html代码检测</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093260108371.jpg" alt=""></p><h3 id="htmltagwrap"><a href="#htmltagwrap" class="headerlink" title="htmltagwrap"></a>htmltagwrap</h3><blockquote><p>可以在选中HTML标签中外面套一层标签</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/vscode-htmltagwrap.gif" alt=""></p><p><strong>使用</strong>:选择一大段代码,然后按“Alt + W”</p><h3 id="Indenticator"><a href="#Indenticator" class="headerlink" title="Indenticator"></a>Indenticator</h3><blockquote><p>突出目前的缩进深度</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/Indenticator.gif" alt=""></p><h3 id="IntelliSense-for-CSS-class-names"><a href="#IntelliSense-for-CSS-class-names" class="headerlink" title="IntelliSense for CSS class names"></a>IntelliSense for CSS class names</h3><p>智能提示 css 的 class 名</p><h3 id="Image-Preview"><a href="#Image-Preview" class="headerlink" title="Image Preview"></a>Image Preview</h3><blockquote><p>鼠标移到路径里显示图像预览</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093708776977.jpg" alt=""></p><h3 id="JavaScript-ES6-code-snippets"><a href="#JavaScript-ES6-code-snippets" class="headerlink" title="JavaScript (ES6) code snippets"></a>JavaScript (ES6) code snippets</h3><blockquote><p>es6代码片段(必备)</p></blockquote><h3 id="JavaScript-Snippet-Pack"><a href="#JavaScript-Snippet-Pack" class="headerlink" title="JavaScript Snippet Pack"></a>JavaScript Snippet Pack</h3><blockquote><p>js代码片段(必备)</p></blockquote><h3 id="jQuery-Code-Snippets"><a href="#jQuery-Code-Snippets" class="headerlink" title="jQuery Code Snippets"></a>jQuery Code Snippets</h3><blockquote><p>jQuery 代码片段</p></blockquote><h3 id="Live-Sass-Compiler"><a href="#Live-Sass-Compiler" class="headerlink" title="Live Sass Compiler"></a>Live Sass Compiler</h3><blockquote><p>实时编译 sass ,不过需要配置,附上我的配置</p></blockquote><figure class="highlight 1c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="string">"liveSassCompile.settings.formats"</span>:[</div><div class="line"> <span class="comment">// You can add more</span></div><div class="line"> {</div><div class="line"> <span class="string">"format"</span>: <span class="string">"compressed"</span>,<span class="comment">//压缩</span></div><div class="line"> <span class="string">"extensionName"</span>: <span class="string">".min.css"</span>,<span class="comment">//编译后缀名</span></div><div class="line"> <span class="string">"savePath"</span>: <span class="string">"./css"</span><span class="comment">//编译保存的路径</span></div><div class="line"> }</div><div class="line"> ],</div></pre></td></tr></table></figure><p><strong>使用</strong></p><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093281194279.jpg" alt=""></p><h3 id="markdownlint"><a href="#markdownlint" class="headerlink" title="markdownlint"></a>markdownlint</h3><blockquote><p>markdown 语法检查</p></blockquote><h3 id="Node-js-Modules-Intellisense"><a href="#Node-js-Modules-Intellisense" class="headerlink" title="Node.js Modules Intellisense"></a>Node.js Modules Intellisense</h3><blockquote><p>可以在导入语句中自动完成JavaScript / TypeScript模块。</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/vscode-node.gif" alt=""></p><h3 id="npm-Intellisense"><a href="#npm-Intellisense" class="headerlink" title="npm Intellisense"></a>npm Intellisense</h3><blockquote><p>在导入语句中自动填充npm模块,跟Node.js Modules Intellisense差不多</p></blockquote><h3 id="open-in-browser"><a href="#open-in-browser" class="headerlink" title="open in browser"></a>open in browser</h3><blockquote><p>当前的 html 文件用浏览器打开,类似 webstorm 的那四个小浏览器图标功能,前提条件html 文件必须保存</p></blockquote><p><strong>快捷键alt+b</strong></p><h3 id="Output-Colorizer"><a href="#Output-Colorizer" class="headerlink" title="Output Colorizer"></a>Output Colorizer</h3><blockquote><p>输出提示的文字颜色有一些变化,方便获取关键信息</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093384952721.jpg" alt=""></p><h3 id="Path-Intellisense"><a href="#Path-Intellisense" class="headerlink" title="Path Intellisense"></a>Path Intellisense</h3><blockquote><p>路径自动补全(必备)</p></blockquote><h3 id="Prettier"><a href="#Prettier" class="headerlink" title="Prettier"></a>Prettier</h3><blockquote><p>格式化JavaScript / TypeScript / CSS 。</p></blockquote><h3 id="Project-Manager"><a href="#Project-Manager" class="headerlink" title="Project Manager"></a>Project Manager</h3><blockquote><p>工程项目过多时,shift+cmd+p(shift+ctrl+p) 然后输入project,第一次选择edit Project编辑自己的工程项目,之后就可以直接选择open打开你的项目</p></blockquote><h3 id="Sass"><a href="#Sass" class="headerlink" title="Sass"></a>Sass</h3><blockquote><p>写 sass 必备</p></blockquote><h3 id="vscode-faker"><a href="#vscode-faker" class="headerlink" title="vscode-faker"></a>vscode-faker</h3><blockquote><p>生成假数据,地址,电话,图片等等</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/vscode-faker.gif" alt=""></p><p>打开方式shift+cmd+p(shift+ctrl+p)) 然后输入faker 就可以选择了</p><h3 id="Quokka-js"><a href="#Quokka-js" class="headerlink" title="Quokka.js"></a>Quokka.js</h3><blockquote><p>实时观看 javascript 的变量的变化</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/vscode-quokka.gif" alt=""></p><p><strong>使用</strong>:先shift+cmd+p (ctrl+shift+p)输入 quokka 选择 new javascript 就行了😀</p><h3 id="Regex-Previewer"><a href="#Regex-Previewer" class="headerlink" title="Regex Previewer"></a>Regex Previewer</h3><blockquote><p>测试正则的插件</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093406954760.jpg" alt=""></p><h3 id="TSLint"><a href="#TSLint" class="headerlink" title="TSLint"></a>TSLint</h3><blockquote><p>检查typescript编程时的语法错误语法</p></blockquote><h3 id="TypeScript-Importer"><a href="#TypeScript-Importer" class="headerlink" title="TypeScript Importer"></a>TypeScript Importer</h3><blockquote><p>自动搜索工作区文件中的TypeScript定义,并将所有已知符号作为完成项,以允许代码完成。</p></blockquote><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/vscode-ts.gif" alt=""></p><h3 id="vscode-icons"><a href="#vscode-icons" class="headerlink" title="vscode-icons"></a>vscode-icons</h3><blockquote><p>目录树图标</p></blockquote><h2 id="react"><a href="#react" class="headerlink" title="react"></a>react</h2><h3 id="React-Native-React-Redux-snippets-for-es6-es7"><a href="#React-Native-React-Redux-snippets-for-es6-es7" class="headerlink" title="React-Native/React/Redux snippets for es6/es7"></a>React-Native/React/Redux snippets for es6/es7</h3><blockquote><p>react代码片段,下载人数超多😉</p></blockquote><h3 id="react-beautify"><a href="#react-beautify" class="headerlink" title="react-beautify"></a>react-beautify</h3><blockquote><p>格式化 javascript, JSX, typescript, TSX 文件</p></blockquote><h2 id="vue"><a href="#vue" class="headerlink" title="vue"></a>vue</h2><h3 id="vetur"><a href="#vetur" class="headerlink" title="vetur"></a>vetur</h3><blockquote><p>语法高亮、智能感知</p></blockquote><h3 id="VueHelper"><a href="#VueHelper" class="headerlink" title="VueHelper"></a>VueHelper</h3><blockquote><p>vue代码片段</p></blockquote><h3 id="Vue-TypeScript-Snippets"><a href="#Vue-TypeScript-Snippets" class="headerlink" title="Vue TypeScript Snippets"></a>Vue TypeScript Snippets</h3><blockquote><p>vue的 typescript 代码片段</p></blockquote><h3 id="Vue-2-Snippets"><a href="#Vue-2-Snippets" class="headerlink" title="Vue 2 Snippets"></a>Vue 2 Snippets</h3><blockquote><p>vue 2代码片段</p></blockquote><h2 id="主题"><a href="#主题" class="headerlink" title="主题"></a>主题</h2><h3 id="Dracula-Official"><a href="#Dracula-Official" class="headerlink" title="Dracula Official"></a>Dracula Official</h3><p>个人最喜欢的主题,应该是最好看的主题之一😀</p><p><img src="https://ws4.sinaimg.cn/large/006tNc79ly1fkzxpf6tevj30l206s74d.jpg" alt=""></p><h3 id="One-Dark-Pro"><a href="#One-Dark-Pro" class="headerlink" title="One Dark Pro"></a>One Dark Pro</h3><p>这个也好看😄</p><p><img src="https://ws3.sinaimg.cn/large/006tNc79ly1fkzy0i7rutj30py06yglq.jpg" alt=""></p><h3 id="Atom-One-Dark-Theme-老版本"><a href="#Atom-One-Dark-Theme-老版本" class="headerlink" title="Atom One Dark Theme(老版本)"></a>Atom One Dark Theme(老版本)</h3><p>这个和 One Dark Pro差不多,One Dark Pro颜色主题多一些</p><p><img src="https://ws3.sinaimg.cn/large/006tNc79ly1fkzxzhoqkrj30l6062jrl.jpg" alt=""></p><h3 id="One-Monokai-Theme"><a href="#One-Monokai-Theme" class="headerlink" title="One Monokai Theme"></a>One Monokai Theme</h3><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093223049049.jpg" alt=""></p><h3 id="Eva-Theme"><a href="#Eva-Theme" class="headerlink" title="Eva Theme"></a>Eva Theme</h3><p>里面包含黑色和白色主题,这个白色主题感觉挺好看的</p><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093223899355.jpg" alt=""></p><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093223624542.jpg" alt=""></p><h3 id="Boxy-Theme-Kit"><a href="#Boxy-Theme-Kit" class="headerlink" title="Boxy Theme Kit"></a>Boxy Theme Kit</h3><p><img src="https://blog-1257878287.cos.ap-chengdu.myqcloud.com/15093265368328.jpg" alt=""></p><hr><p>大家还有什么好的插件推荐吗🤓</p>]]></content>
<summary type="html">
<hr>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="插件" scheme="http://www.shenzekun.cn/tags/%E6%8F%92%E4%BB%B6/"/>
</entry>
<entry>
<title>js 的 call 与 apply 速度对比</title>
<link href="http://www.shenzekun.cn/js-%E7%9A%84-call-%E4%B8%8E-apply-%E9%80%9F%E5%BA%A6%E5%AF%B9%E6%AF%94.html"/>
<id>http://www.shenzekun.cn/js-的-call-与-apply-速度对比.html</id>
<published>2017-10-20T04:37:57.000Z</published>
<updated>2018-10-21T12:51:44.841Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>最近在看 underscore 的源码时发现,作者好多都用 call,而用 apply 比较少,比如说下面这段:👇</p></blockquote><a id="more"></a><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> optimizeCb = <span class="function"><span class="keyword">function</span>(<span class="params">func, context, argCount</span>) </span>{</div><div class="line"> <span class="comment">// 如果没有指定 this 指向,则返回原函数</span></div><div class="line"> <span class="keyword">if</span> (context === <span class="keyword">void</span> <span class="number">0</span>) <span class="keyword">return</span> func;</div><div class="line"></div><div class="line"> <span class="keyword">switch</span> (argCount == <span class="literal">null</span> ? <span class="number">3</span> : argCount) {</div><div class="line"> <span class="keyword">case</span> <span class="number">1</span>:</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>{</div><div class="line"> <span class="keyword">return</span> func.call(context, value);</div><div class="line"> };</div><div class="line"> <span class="keyword">case</span> <span class="number">2</span>:</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">value, other</span>) </span>{</div><div class="line"> <span class="keyword">return</span> func.call(context, value, other);</div><div class="line"> };</div><div class="line"></div><div class="line"> <span class="comment">// 如果有指定 this,但没有传入 argCount 参数</span></div><div class="line"> <span class="comment">// 则执行以下 case</span></div><div class="line"> <span class="keyword">case</span> <span class="number">3</span>:</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">value, index, collection</span>) </span>{</div><div class="line"> <span class="keyword">return</span> func.call(context, value, index, collection);</div><div class="line"> };</div><div class="line"> <span class="keyword">case</span> <span class="number">4</span>:</div><div class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">accumulator, value, index, collection</span>) </span>{</div><div class="line"> <span class="keyword">return</span> func.call(context, accumulator, value, index, collection);</div><div class="line"> };</div><div class="line"> }</div><div class="line">};</div></pre></td></tr></table></figure><p>既然 call 和 apply 都能用,那为什么只用 call 而不用 apply 呢?<br>经过网上的搜索发现,<strong>call 比 apply 速度快</strong>,在 console运行如下代码:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">x</span>(<span class="params">a,b</span>) </span>{}</div><div class="line"><span class="keyword">var</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</div><div class="line"><span class="built_in">console</span>.time(<span class="string">"call"</span>);</div><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">1000000</span>; i++) {</div><div class="line"> x.call(<span class="keyword">this</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</div><div class="line">}</div><div class="line"><span class="built_in">console</span>.timeEnd(<span class="string">"call"</span>);</div><div class="line"></div><div class="line"><span class="built_in">console</span>.time(<span class="string">"apply"</span>);</div><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j < <span class="number">1000000</span>; j++) {</div><div class="line"> x.apply(<span class="keyword">this</span>, a);</div><div class="line">}</div><div class="line"><span class="built_in">console</span>.timeEnd(<span class="string">"apply"</span>);</div></pre></td></tr></table></figure><p>console的结果:</p><p><img src="http://upload-images.jianshu.io/upload_images/5308475-2f25092ecb658b17.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>可以发现 call 比 apply 快了10ms 左右,那是什么原因造成这样的呢?<br><strong>因为 apply 运行前要对作为参数的数组进行一系列检验和深拷贝,而 call 则没有</strong><br>我们看一下 ECMAScript 是怎么写的:</p><p><img src="http://upload-images.jianshu.io/upload_images/5308475-40f7c9167b3a338d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p><img src="http://upload-images.jianshu.io/upload_images/5308475-93025d076479f624.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><p>由ECMAScript 标准发现 apply 比 call 的步骤多了好多,这就是 call 比 apply 执行速度快的原因!</p>]]></content>
<summary type="html">
<hr><blockquote><p>最近在看 underscore 的源码时发现,作者好多都用 call,而用 apply 比较少,比如说下面这段:👇</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="js" scheme="http://www.shenzekun.cn/tags/js/"/>
<category term="前端" scheme="http://www.shenzekun.cn/tags/%E5%89%8D%E7%AB%AF/"/>
</entry>
<entry>
<title>Vimium 常用快捷键</title>
<link href="http://www.shenzekun.cn/Vimium-%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE.html"/>
<id>http://www.shenzekun.cn/Vimium-常用快捷键.html</id>
<published>2017-10-19T10:38:39.000Z</published>
<updated>2018-10-21T12:51:44.844Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>最近用了谷歌插件vimium,发现非常好用,牛逼神器一个😄,下面记一下常用的快捷键</p></blockquote><a id="more"></a><p>打开 <code>vimium help</code> 快捷键 shift+/</p><p><img src="http://upload-images.jianshu.io/upload_images/5308475-8ae19353b092aa14.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p><ul><li><strong>j</strong>: 向下移动</li><li><strong>k</strong>: 向上移动</li><li><strong>h</strong>: 左移</li><li><strong>l</strong> : 右移</li><li><strong>d</strong>:向下移动一个页面</li><li><strong>u</strong>:向上移动一个页面</li><li><strong>gg</strong> : 移到页面顶部</li><li><strong>G</strong> : 移到页面底部</li><li><strong>x</strong>:关闭当前标签页</li><li><strong>X</strong>:恢复关闭的标签页</li><li><strong>H</strong>:回到上一个历史页面</li><li><strong>L</strong>:回到下一个历史页面</li><li><strong>J</strong>:跳到左边标签页</li><li><strong>K</strong>:跳到右边标签页</li><li><strong>r</strong> : 刷新</li><li><strong>gs</strong> : 查看网页源码</li><li><strong>yy</strong> : 复制当前页面的 url 到剪切板</li><li><strong>f</strong>: 在当前标签页中打开页面有的链接</li><li><strong>F</strong>:在新标签页中页面有的链接</li><li><strong>p</strong>:在当前标签页中打开剪切板中的链接</li><li><strong>P</strong>:在新的标签页中打开剪切板中的链接</li><li><strong>t</strong>:创建新的标签页(也就是 command+t)</li><li><strong>T</strong>:搜索打开的标签页</li><li><strong>b</strong> : 打开书签</li><li><strong>B</strong> : 在新标签中打开书签</li><li><strong>/</strong> :查找(相当于 command+f)</li><li><strong>i</strong> : 进入输入模式(这个不知道要干嘛的( ⊙o⊙?))</li><li><strong>esc</strong>:退出输入模式</li><li><strong>o</strong> : 在当前页面中打开URL,书签和历史记录</li><li><strong>O</strong> : 在新标签页面中打开URL,书签和历史记录</li><li><strong>gi</strong>:将焦点集中到第一个输入框</li></ul>]]></content>
<summary type="html">
<hr><blockquote><p>最近用了谷歌插件vimium,发现非常好用,牛逼神器一个😄,下面记一下常用的快捷键</p></blockquote>
</summary>
<category term="技巧" scheme="http://www.shenzekun.cn/categories/%E6%8A%80%E5%B7%A7/"/>
<category term="技巧" scheme="http://www.shenzekun.cn/tags/%E6%8A%80%E5%B7%A7/"/>
</entry>
<entry>
<title>前端学习笔记之观察者模式</title>
<link href="http://www.shenzekun.cn/%E5%89%8D%E7%AB%AF%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8B%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F.html"/>
<id>http://www.shenzekun.cn/前端学习笔记之观察者模式.html</id>
<published>2017-10-05T07:05:59.000Z</published>
<updated>2018-10-21T12:51:44.848Z</updated>
<content type="html"><![CDATA[<hr><blockquote><p>观察者模式也称”发布-订阅”模式,它的作用就是当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,自动刷新对象状态</p></blockquote><a id="more"></a><p>举个生活比较常见常见的例子,比如你去面试之后,面试官看你表现不错,最后会跟你要联系方式,以便之后可以联系你。在这角色扮演当中,你就是“订阅者”,面试官就是“发布者”。</p><p>那么发布订阅模式是咋实现的呢?</p><p><strong>思路</strong></p><ol><li>给定一个发布者</li><li>面试者将联系方式给发布者</li><li>发布者的一个列表有各种职位(web端的,java 的),里面记载回调函数以便通知这些面试者</li><li>最后发布消息的时候,会遍历这个列表的职位的回调函数,告诉面试者面试这个职位是通过还是不通过</li><li>如果面试者取消了订阅,那么将回调函数和之前的回调函数作对比,如果相等,就将这个面试者的上班通知去掉</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> Event = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> events = {}; <span class="comment">//发布者</span></div><div class="line"></div><div class="line"> <span class="comment">//subscribe也就是订阅,post 代表面试者要面的职位,callback表示为回调函数</span></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">subscribe</span>(<span class="params">post, callback</span>) </span>{</div><div class="line"> events[post] = events[post] || []; <span class="comment">//发布者的列表里有没有这个面试职位,如果没有就创建一个空数组</span></div><div class="line"> events[post].push(callback);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">//publish 表示发布</span></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">publish</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">var</span> post = <span class="built_in">Array</span>.prototype.shift.call(<span class="built_in">arguments</span>); <span class="comment">//第一个参数指定“键”</span></div><div class="line"> <span class="keyword">var</span> fns = events[post]; <span class="comment">//设置缓存,提高性能</span></div><div class="line"> <span class="keyword">if</span> (!fns) { <span class="comment">//如果发布者的列表里没有这个职位,那肯定是不能发布</span></div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < fns.length; i++) { <span class="comment">//遍历当前的职位的数组里有几个面试者</span></div><div class="line"> fns[i].apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">//unsubscribe 表示取消订阅</span></div><div class="line"> <span class="function"><span class="keyword">function</span> <span class="title">unsubscribe</span>(<span class="params">post, fn</span>) </span>{</div><div class="line"> <span class="keyword">var</span> fns = events[post];</div><div class="line"> <span class="keyword">if</span> (fns) {</div><div class="line"> <span class="keyword">if</span> (fn) {</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = fns.length; i >= <span class="number">0</span>; i--) {</div><div class="line"> <span class="keyword">if</span> (fns[i] === fn) fns.splice(i, <span class="number">1</span>);</div><div class="line"> }</div><div class="line"> } <span class="keyword">else</span> {<span class="comment">//如果没有传入fn回调函数,直接取消post对应消息的所有订阅</span></div><div class="line"> fns = [];</div><div class="line"> }</div><div class="line"> } <span class="keyword">else</span> {<span class="comment">//如果发布者的列表没有这个职位,直接 return</span></div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">subscribe</span>: subscribe,</div><div class="line"> <span class="attr">publish</span>: publish,</div><div class="line"> <span class="attr">unsubscribe</span>: unsubscribe</div><div class="line"> };</div><div class="line">})();</div></pre></td></tr></table></figure><p>测试:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> fn1 = <span class="function"><span class="keyword">function</span>(<span class="params">time</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"小明你通过了面试,上班时间:"</span> + time);</div><div class="line">};</div><div class="line"><span class="keyword">var</span> fn2 = <span class="function"><span class="keyword">function</span>(<span class="params">time</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">"小强你通过了面试,上班时间:"</span> + time);</div><div class="line">};</div><div class="line"><span class="comment">//小明将联系方式给了发布者,发布者(hr)觉得小明不错,可以通过,于是在列表(java)里写下了一些回调函数,到时候发布的时候将上班时间告诉小明</span></div><div class="line">Event.subscribe(<span class="string">"java"</span>, fn1);</div><div class="line"><span class="comment">//小强也订阅了</span></div><div class="line">Event.subscribe(<span class="string">"java"</span>, fn2);</div><div class="line"></div><div class="line">Event.publish(<span class="string">"java"</span>, <span class="string">"2017-10-01"</span>);</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">/*输出:</span></div><div class="line">小明你通过了面试,上班时间:2017-10-01</div><div class="line">小强你通过了面试,上班时间:2017-10-01</div><div class="line">*/</div><div class="line"></div><div class="line">Event.unsubscribe(<span class="string">"java"</span>, fn1);<span class="comment">//删除小明的上班通知</span></div><div class="line">Event.publish(<span class="string">"java"</span>, <span class="string">"2017-10-01"</span>);</div><div class="line"></div><div class="line"><span class="comment">/*输出:</span></div><div class="line">小强你通过了面试,上班时间:2017-10-01</div><div class="line">*/</div></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html" target="_blank" rel="external">Javascript异步编程的4种方法</a><br><a href="https://segmentfault.com/a/1190000007248460" target="_blank" rel="external">js设计模式笔记 - 观察者模式</a></p>]]></content>
<summary type="html">
<hr><blockquote><p>观察者模式也称”发布-订阅”模式,它的作用就是当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,自动刷新对象状态</p></blockquote>
</summary>
<category term="前端" scheme="http://www.shenzekun.cn/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="设计模式" scheme="http://www.shenzekun.cn/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
</entry>
</feed>