-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
398 lines (356 loc) · 81.1 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>TypeScript-02</title>
<url>/2020/06/19/TypeScript-02/</url>
<content><![CDATA[<h4 id="枚举类型"><a href="#枚举类型" class="headerlink" title="枚举类型"></a>枚举类型</h4><h6 id="数字枚举"><a href="#数字枚举" class="headerlink" title="数字枚举"></a>数字枚举</h6><figure class="highlight ts"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> Roles {</span><br><span class="line"> Reporter,</span><br><span class="line"> Developer,</span><br><span class="line"> Maintainer,</span><br><span class="line"> Owner,</span><br><span class="line"> Guest,</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>我们可以直接访问枚举对象中的值,通过<code>.</code>操作符,返回的是当前属性的索引,默认从<code>0</code>开始。数字枚举具体的实现是通过反向映射来实现。</p>
<h6 id="字符串枚举"><a href="#字符串枚举" class="headerlink" title="字符串枚举"></a>字符串枚举</h6><figure class="highlight ts"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> Message {</span><br><span class="line"> Success = <span class="string">"成功了"</span>,</span><br><span class="line"> Fail = <span class="string">"失败了"</span>,</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>同样我们可以直接通过<code>.</code>运算符取值,但是不同的是,字符串枚举没有实现反向映射</p>
<h6 id="异构枚举"><a href="#异构枚举" class="headerlink" title="异构枚举"></a>异构枚举</h6><p>同时使用数字枚举和字符串枚举,可以实现一个异构枚举,但是异构枚举容易引起人的混淆,所以一般情况不建议使用</p>
<figure class="highlight ts"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> Answer {</span><br><span class="line"> N,</span><br><span class="line"> Y = <span class="string">"Yes"</span>,</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h6 id="枚举类型分类"><a href="#枚举类型分类" class="headerlink" title="枚举类型分类"></a>枚举类型分类</h6><figure class="highlight ts"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> Char {</span><br><span class="line"> a, <span class="comment">// 常量枚举,没有初始值</span></span><br><span class="line"> b = Char.a, <span class="comment">// 对已有枚举成员进行引用</span></span><br><span class="line"> c = <span class="number">1</span> + <span class="number">3</span>, <span class="comment">// 常量表达式</span></span><br><span class="line"> <span class="comment">// computed</span></span><br><span class="line"> d = <span class="built_in">Math</span>.random() <span class="comment">// 常量表达式,不会在编译阶段进行计算</span></span><br><span class="line"> f = <span class="number">4</span> <span class="comment">// 计算枚举后面的成员需要有初始值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>常量枚举会在编译被移除,当我们不需要一个对象时,但是需要对象值的时候可以使用,这样可以减少我们编译后的代码量,具体需要使用<code>const</code></p>
</blockquote>
<figure class="highlight ts"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="keyword">enum</span> Test {</span><br><span class="line"> Jan,</span><br><span class="line"> Fed,</span><br><span class="line"> Mar,</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h6 id="枚举类型-1"><a href="#枚举类型-1" class="headerlink" title="枚举类型"></a>枚举类型</h6><ul>
<li><code>enum E {A, B}</code>: 枚举类型没有初始值</li>
<li><code>enum F {a = 0, b = 1}</code> :枚举类型默认都是数值</li>
<li><code>enum G {a = 'apple', b = 'banana'}</code>: 字符串枚举类型</li>
</ul>
<blockquote>
<p>不同类型得到枚举成员不能进行比较</p>
</blockquote>
]]></content>
<categories>
<category>TypeScript</category>
</categories>
<tags>
<tag>TypeScript</tag>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title>TypeScript-01</title>
<url>/2020/06/19/TypeScript-01/</url>
<content><![CDATA[<h4 id="静态语言与动态语言的对比"><a href="#静态语言与动态语言的对比" class="headerlink" title="静态语言与动态语言的对比"></a>静态语言与动态语言的对比</h4><table>
<thead>
<tr>
<th>静态类型语言</th>
<th>动态类型语言</th>
</tr>
</thead>
<tbody><tr>
<td>对类型极其严格</td>
<td>对类型非常宽松</td>
</tr>
<tr>
<td>立即发现错误</td>
<td><code>BUG</code>隐藏时间久</td>
</tr>
<tr>
<td>运行效率高,性能好</td>
<td>运行效率低,性能差</td>
</tr>
<tr>
<td>自文档化</td>
<td>可读性差</td>
</tr>
</tbody></table>
<h4 id="类型"><a href="#类型" class="headerlink" title="类型"></a>类型</h4><p><code>ES6</code>数据类型</p>
<ul>
<li>Boolean,</li>
<li>Number,</li>
<li>String,</li>
<li>Array,</li>
<li>Function</li>
<li>Object</li>
<li>null</li>
<li>undedfined</li>
<li>Symbol</li>
</ul>
<p><code>TypeScript</code>新增数据类型</p>
<ul>
<li>void<ul>
<li>表示没有任何返回值的类型</li>
</ul>
</li>
<li>any<ul>
<li>不指定变量类型,那么它的类型就是<code>any</code>类型,可以任意赋值</li>
</ul>
</li>
<li>never<ul>
<li>永远不会有返回值的类型</li>
</ul>
</li>
<li>元组<ul>
<li>可以通过<code>push</code>方法添加成员</li>
<li>通过 push 添加进去的成员无法访问</li>
</ul>
</li>
<li>枚举</li>
<li>高级类型</li>
</ul>
<h6 id="类型注解"><a href="#类型注解" class="headerlink" title="类型注解"></a>类型注解</h6><ul>
<li>作用:相当于强类型语言中得类型声明</li>
<li>语法:(变量|函数): type</li>
</ul>
<h6 id="特殊的类型"><a href="#特殊的类型" class="headerlink" title="特殊的类型"></a>特殊的类型</h6><ul>
<li>undefined</li>
<li>null</li>
</ul>
<blockquote>
<p>在<code>typescript</code>中,<code>undefined</code>和<code>null</code>是任何类型的子类,不可以讲这两个类型赋值给其他类型,但是可以讲其他类型赋值给这两个类型。需要在编辑器中配置<code>tsconfig.json</code>,<code>strictNullChecks: false</code>在配合联合类型使用</p>
</blockquote>
]]></content>
<categories>
<category>TypeScript</category>
</categories>
<tags>
<tag>TypeScript</tag>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title>koa+mysql搭建项目环境</title>
<url>/2020/05/24/koa-mysql%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE%E7%8E%AF%E5%A2%83/</url>
<content><![CDATA[<p>在实际项目中,我们更多的会使用<code>mysql</code>来做数据存储,那么如何使用<code>koa</code>和<code>mysql</code>来搭建基础框架呢?</p>
<h4 id="安装依赖"><a href="#安装依赖" class="headerlink" title="安装依赖"></a>安装依赖</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cnpm install koa @koa/router babel-preset-es2015 babel-register koa-bodyparser mysql --save</span><br></pre></td></tr></table></figure>
<h4 id="创建项目结构"><a href="#创建项目结构" class="headerlink" title="创建项目结构"></a>创建项目结构</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">- node_modules // 依赖</span><br><span class="line">- src // 开发目录</span><br><span class="line"> - config //基本配置文件目录</span><br><span class="line"> - default.js // 数据库、端口等配置</span><br><span class="line"> - controllers // 控制器</span><br><span class="line"> - user.js // 用户查询控制器</span><br><span class="line"> - mysql // 数据库连接</span><br><span class="line"> - db.js // 创建连接</span><br><span class="line"> - query.js // 封装查询函数</span><br><span class="line"> - app.js // 项目主文件</span><br><span class="line"> - router.js // 路由接口</span><br><span class="line"> - start.js // 项目入口文件</span><br></pre></td></tr></table></figure>
<h4 id="文件说明"><a href="#文件说明" class="headerlink" title="文件说明"></a>文件说明</h4><p><code>config/default.js</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> config = {</span><br><span class="line"> port: <span class="number">3000</span>, <span class="comment">// 启动端口</span></span><br><span class="line"> database: {</span><br><span class="line"> <span class="comment">// 数据库配置</span></span><br><span class="line"> database: <span class="string">"test"</span>,</span><br><span class="line"> host: <span class="string">"127.0.0.1"</span>,</span><br><span class="line"> user: <span class="string">"root"</span>,</span><br><span class="line"> password: <span class="string">"123456"</span>,</span><br><span class="line"> minInterval: <span class="number">200</span>,</span><br><span class="line"> acquireTimeout: <span class="number">300</span>,</span><br><span class="line"> connectionLimit: <span class="number">100</span>,</span><br><span class="line"> connectTimeout: <span class="number">2000</span>,</span><br><span class="line"> queueLimit: <span class="number">0</span>,</span><br><span class="line"> debug: <span class="literal">true</span>,</span><br><span class="line"> waitForConnection: <span class="literal">true</span>,</span><br><span class="line"> multipleStatements: <span class="literal">true</span>, <span class="comment">// 允许多条sql同时执行</span></span><br><span class="line"> acquireTimeout: <span class="number">5000</span>,</span><br><span class="line"> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>mysql/db.js</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> mysql <span class="keyword">from</span> <span class="string">"mysql"</span></span><br><span class="line"><span class="keyword">import</span> config <span class="keyword">from</span> <span class="string">"../config/default.js"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> connection = mysql.createConnection(config.databse)</span><br><span class="line">connection.connect()</span><br></pre></td></tr></table></figure>
<p><code>mysql/query.js</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> mysql <span class="keyword">from</span> <span class="string">"mysql"</span></span><br><span class="line"><span class="keyword">import</span> config <span class="keyword">from</span> <span class="string">"../config/default"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> pool = mysql.createPool(config.database) <span class="comment">// 创建连接池</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// query sql语句入口</span></span><br><span class="line"><span class="keyword">const</span> query = <span class="function">(<span class="params">sql, values</span>) =></span> {</span><br><span 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, reject</span>) =></span> {</span><br><span class="line"> pool.getConnection(<span class="function">(<span class="params">err, connection</span>) =></span> {</span><br><span class="line"> <span class="comment">// 调用已经创建好了的连接池,因为创建每个连接池的时候是非常消耗资源和时间的</span></span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> reject(err)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> connection.query(sql, values, (err, fields) => {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> reject(err)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> resolve(fields)</span><br><span class="line"> }</span><br><span class="line"> connection.release() <span class="comment">// 执行完成后释放连接池,避免占用连接池资源</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>controllers/user.js</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="meta">"use strict"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> db <span class="keyword">from</span> <span class="string">"../mysql/query"</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">this</span>.queryUsers = <span class="keyword">this</span>.queryUsers.bind(<span class="keyword">this</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">async</span> queryUsers(ctx) {</span><br><span class="line"> <span class="keyword">let</span> sql = <span class="string">"SELECT * FROM user"</span></span><br><span class="line"> <span class="keyword">await</span> db</span><br><span class="line"> .query(sql)</span><br><span class="line"> .then(<span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (res && res.length) {</span><br><span class="line"> ctx.body = {</span><br><span class="line"> code: <span class="number">20000</span>,</span><br><span class="line"> message: <span class="string">"查询成功"</span>,</span><br><span class="line"> data: res,</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> ctx.body = {</span><br><span class="line"> code: <span class="number">50001</span>,</span><br><span class="line"> message: <span class="string">"查询失败"</span>,</span><br><span class="line"> data: <span class="literal">null</span>,</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> .catch(<span class="function">(<span class="params">e</span>) =></span> {</span><br><span class="line"> ctx.body = {</span><br><span class="line"> code: <span class="number">50002</span>,</span><br><span class="line"> message: <span class="string">"服务器内部错误"</span>,</span><br><span class="line"> data: <span class="literal">null</span>,</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>app.js</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> Koa <span class="keyword">from</span> <span class="string">"koa"</span></span><br><span class="line"><span class="keyword">import</span> router <span class="keyword">from</span> <span class="string">"./router"</span></span><br><span class="line"><span class="keyword">import</span> db <span class="keyword">from</span> <span class="string">"./mysql/db"</span></span><br><span class="line"><span class="keyword">import</span> Bodyparser <span class="keyword">from</span> <span class="string">"koa-bodyparser"</span></span><br><span class="line"><span class="keyword">import</span> config <span class="keyword">from</span> <span class="string">"./config/default"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"></span><br><span class="line">app.use(Bodyparser()).use(router.routes()).use(router.allowedMethods())</span><br><span class="line"></span><br><span class="line">app.listen(config.port, () => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`服务已启动,端口:<span class="subst">${config.port}</span>`</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p><code>router.js</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> Router <span class="keyword">from</span> <span class="string">"@koa/router"</span></span><br><span class="line"><span class="keyword">import</span> UserController <span class="keyword">from</span> <span class="string">"./controllers/user"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> Router()</span><br><span class="line"></span><br><span class="line"><span class="comment">// user路由</span></span><br><span class="line">router.post(<span class="string">"/user"</span>, UserController.queryUsers)</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> router</span><br></pre></td></tr></table></figure>
<p><code>start.js</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 之所以用这个启动文件,是为了使用register来让我们的项目支持ES6以及ES7的一些语法,例如import、export、async、await等</span></span><br><span class="line"><span class="comment">// 生产项目中不建议使用babel-register来这么处理,因为相当于每次进入文件的时候都需要用babel-register来进行文件转换,性能会大打折扣</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">require</span>(<span class="string">"babel-register"</span>)({</span><br><span class="line"> presets: [<span class="string">"es2015"</span>],</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="built_in">require</span>(<span class="string">"./app"</span>)</span><br></pre></td></tr></table></figure>
<p>这样我们的项目基础框架就搭建完成了,接下来就可以进行更多的接口开发了,更多的高级用法后续我们会集成进来。例如:<code>JWT</code>鉴权、<code>redis</code>缓存、文件上传、压缩等等</p>
]]></content>
<categories>
<category>node</category>
</categories>
<tags>
<tag>node</tag>
<tag>koa</tag>
</tags>
</entry>
<entry>
<title>node基础学习(一)</title>
<url>/2020/07/24/node%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0-%E4%B8%80/</url>
<content><![CDATA[<h4 id="File-部分"><a href="#File-部分" class="headerlink" title="File 部分"></a>File 部分</h4><h5 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"></span><br><span class="line">fs.readFile(<span class="string">'filepath'</span>, (err, data) {</span><br><span class="line"> <span class="keyword">if</span>(err) {</span><br><span class="line"> res.write(<span class="string">"错了"</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> res.write(data)</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<ol>
<li><p>本地通过 localhost:port/xxx.html 访问服务器失败,报跨域问题或者本地资源不能加载问题或者请求失败问题<br>解决方案:因为本地开启的只是一个 node 服务,浏览器不能通过 node 服务去读取本地静态资源,需要通过<code>fs</code>模块去先读取静态文件,然后通过 node 服务器返回,这样才能读取。</p>
</li>
<li><p>readFile 读取本地资源路径问题<br>解决方案:一般我们将静态资源会统一放在一个文件夹里面,而不是直接跟服务器文件放在一个同级目录,所以一般我们在通过<code>readFile</code>方法读取资源时,如果静态文件在服务器文件的同级目录,则<code>filepath</code>通过<code>./xxx.xx</code>去访问,如果静态文件是在统一的一个文件夹内,则需要加上文件夹目录名称</p>
</li>
</ol>
<h4 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h4><h5 id="为什么需要缓存"><a href="#为什么需要缓存" class="headerlink" title="为什么需要缓存"></a>为什么需要缓存</h5><p>减少服务器带宽,这样可以节约客户端访问带来的流量问题。不然带宽成本高,所以需要通过来缓存常用的或者不太修改的一些文件</p>
<h5 id="缓存命中率"><a href="#缓存命中率" class="headerlink" title="缓存命中率"></a>缓存命中率</h5><p>终端用户访问加速节点时,如果该节点有缓存住了要被访问的数据时就叫做命中,如果没有的话需要回原服务器取,就是没有命中。取数据的过程与用户访问是同步进行的,所以即使是重新取的新数据,用户也不会感觉到有延时。 命中率=命中数/(命中数+没有命中数), 缓存命中率是判断加速效果好坏的重要因素之一。</p>
<h5 id="缓存策略"><a href="#缓存策略" class="headerlink" title="缓存策略"></a>缓存策略</h5><p>通过<code>cache-control</code>,可以设置缓存或者不缓存</p>
<p>设置缓存步骤:</p>
<ul>
<li>通过<code>fs</code>模块提供的<code>stat</code>方法来获取到客户端访问的当前文件的各种信息</li>
<li>如果发生错误,则证明客户端访问的文件不存在,否则判断当前接收到的请求头有没有<code>if-modified-since</code>属性。</li>
<li>如果有,则证明已经获取到客户端当前文件的最后一次修改时间,再与服务器端当前文件的最后一次修改时间进行比较</li>
<li>如果服务度端的时间较新,则将当前时间通过设置头文件的<code>Last-Modified</code>属性,来更新客户端文件修改时间</li>
<li>否则就证明当前客户端文件是最新的,不需要重新从服务器端请求,从缓存中获取即可</li>
<li></li>
</ul>
<h4 id="进程与线程"><a href="#进程与线程" class="headerlink" title="进程与线程"></a>进程与线程</h4><ul>
<li>多线程:性能高,复杂,对程序员要求高</li>
<li>多进程:性能略低;简单、对于程序员要求低</li>
</ul>
<p><code>Node</code>默认:单进程、单线程</p>
<p>一般采用主进程负责派生子进程,子进程负责工作。万一主进程崩了,子进程也全部会被杀死</p>
<p>多进程: 安全、性能高</p>
<p>进程:<br>1、普通程序不能“创建”进程,只有系统进程才能创建进程,只有主进程能分裂<br>2、进程是分裂出来的<br>3、分裂出来的两个进程执行的是同一套代码<br>4、父子进程之间可以共享句柄()</p>
<p>多进程工作方式:一个进程先工作,其他进程闲置,等待这个进程满了,再调度第二个进程进行工作</p>
]]></content>
<categories>
<category>node</category>
</categories>
<tags>
<tag>node</tag>
</tags>
</entry>
<entry>
<title>vue-cli3使用vant-ui构建移动端项目</title>
<url>/2020/07/24/vue-cli3%E4%BD%BF%E7%94%A8vant-ui%E6%9E%84%E5%BB%BA%E7%A7%BB%E5%8A%A8%E7%AB%AF%E9%A1%B9%E7%9B%AE/</url>
<content><![CDATA[<h4 id="脚手架"><a href="#脚手架" class="headerlink" title="脚手架"></a>脚手架</h4><figure class="highlight dos"><table><tr><td class="code"><pre><span class="line"># 安装vue-cli3</span><br><span class="line"></span><br><span class="line">npm install -g @vue/cli</span><br><span class="line"></span><br><span class="line"># 创建一个项目</span><br><span class="line"></span><br><span class="line">vue create hello-world</span><br></pre></td></tr></table></figure>
<h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><figure class="highlight dos"><table><tr><td class="code"><pre><span class="line">npm i vant -S</span><br></pre></td></tr></table></figure>
<h4 id="按需引入组件"><a href="#按需引入组件" class="headerlink" title="按需引入组件"></a>按需引入组件</h4><blockquote>
<p><code>babel-plugin-import</code>是一款<code>babel</code>插件,它会在编译过程中将<code>import</code>的写法自动转换为按需引入的方式</p>
</blockquote>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"># 安装方式</span><br><span class="line">npm i babel-plugin-<span class="keyword">import</span> -D</span><br><span class="line"></span><br><span class="line"># 如果使用babel7需要配置babel.config.js</span><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> plugins: [</span><br><span class="line"> [<span class="string">'import'</span>, {</span><br><span class="line"> libraryName: <span class="string">'vant'</span>,</span><br><span class="line"> libraryDirectory: <span class="string">'es'</span>,</span><br><span class="line"> style: <span class="literal">true</span></span><br><span class="line"> }, <span class="string">'vant'</span>]</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="项目中按需使用"><a href="#项目中按需使用" class="headerlink" title="项目中按需使用"></a>项目中按需使用</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># main.js import {Button, Toast} from 'vant' Vue.use(Button, Toast) # 组件中使用</span><br><span class="line">// test.vue</span><br><span class="line"><template></span><br><span class="line"> <div class="test"></span><br><span class="line"> <van-button @click="handleClick">默认按钮</van-button></span><br><span class="line"> </div></span><br><span class="line"></template></span><br></pre></td></tr></table></figure>
<h4 id="rem适配"><a href="#rem适配" class="headerlink" title="rem适配"></a><code>rem</code>适配</h4><blockquote>
<p><code>Vant</code>中的样式默认使用<code>px</code>作为单位,如果需要使用<code>rem</code>单位,推荐使用下面两个工具:</p>
</blockquote>
<ul>
<li><a href="https://github.com/cuth/postcss-pxtorem" target="_blank" rel="noopener">postcss-pxtorem</a>是一款<code>postcss</code>插件,用于将单位转换为<code>rem</code></li>
<li><a href="https://github.com/amfe/lib-flexible" target="_blank" rel="noopener">lib-flexible</a>用于设置<code>rem</code>基准值</li>
</ul>
<h4 id="配置-PostCSS"><a href="#配置-PostCSS" class="headerlink" title="配置 PostCSS"></a>配置 PostCSS</h4><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"># vue.config.js</span><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> pluginOptions: {</span><br><span class="line"> <span class="string">'autoprefixer'</span>: {</span><br><span class="line"> browsers: [<span class="string">'Aandroid >= 4.0'</span>, <span class="string">'iOS >= 7'</span>]</span><br><span class="line"> },</span><br><span class="line"> <span class="string">'postcss-pxtorem'</span>: {</span><br><span class="line"> rootValue: <span class="number">37.5</span>,</span><br><span class="line"> propList: [<span class="string">'*'</span>]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这个时候我们已经可以进行项目预览了,我们再进一步丰富一下框架,加入路由懒加载和代码分割</p>
<h4 id="babel-plugin-syntax-dynamic-import"><a href="#babel-plugin-syntax-dynamic-import" class="headerlink" title="@babel/plugin-syntax-dynamic-import"></a><code>@babel/plugin-syntax-dynamic-import</code></h4><p>配置<code>babel.config.js</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> plugins: [</span><br><span class="line"> <span class="string">'@babel/plugin-syntax-dynamic-import'</span>,</span><br><span class="line"> [<span class="string">'import'</span>, {</span><br><span class="line"> libraryName: <span class="string">'vant'</span>,</span><br><span class="line"> libraryDirectory: <span class="string">'es'</span>,</span><br><span class="line"> style: <span class="literal">true</span></span><br><span class="line"> }, <span class="string">'vant'</span>]</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>vue</category>
</categories>
<tags>
<tag>vue</tag>
<tag>vant-ui</tag>
<tag>vue-cli</tag>
</tags>
</entry>
<entry>
<title>vue-socket-io在客户端的使用</title>
<url>/2020/07/24/vue-socket-io%E5%9C%A8%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%9A%84%E4%BD%BF%E7%94%A8/</url>
<content><![CDATA[<h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">npm i vue-socket.io socket.io-client -S</span><br></pre></td></tr></table></figure>
<h4 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h4><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> VueSocketio <span class="keyword">from</span> <span class="string">"vue-socket.io"</span></span><br><span class="line"></span><br><span class="line">Vue.use(</span><br><span class="line"> <span class="keyword">new</span> VueSocketio({</span><br><span class="line"> debug: <span class="literal">true</span>, <span class="comment">// 开发环境关掉</span></span><br><span class="line"> connection: <span class="string">"http://localhost:3000"</span>, <span class="comment">// 后端socket地址</span></span><br><span class="line"> })</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// chat.vue</span><br><span class="line"><template></span><br><span class="line"> <textarea v-model="message"></textarea></span><br><span class="line"> <button @click="handleSendMessage">send</button></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">export default {</span><br><span class="line"> data() {</span><br><span class="line"> return {</span><br><span class="line"> message: ''</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> mounted() {</span><br><span class="line"> // 进入页面与服务端进行第一次连接</span><br><span class="line"> this.$socket.emit('connection')</span><br><span class="line"> },</span><br><span class="line"> sockets: {</span><br><span class="line"> connect() {</span><br><span class="line"> console.log('连接成功')</span><br><span class="line"> },</span><br><span class="line"> response(data) {</span><br><span class="line"> console.log(`接收到后端返回数据: ${data}`)</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"> handleSendMessage() {</span><br><span class="line"> this.$socket.emit('sendMsg')</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><script></span><br></pre></td></tr></table></figure>
<h4 id="服务端"><a href="#服务端" class="headerlink" title="服务端"></a>服务端</h4><p>服务端使用的<code>koa-socket</code></p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app.js</span></span><br><span class="line"><span class="keyword">const</span> IO = <span class="built_in">require</span>(<span class="string">"koa-socket"</span>)</span><br><span class="line"><span class="keyword">const</span> Koa = <span class="built_in">require</span>(<span class="string">"koa"</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Koa()</span><br><span class="line"><span class="keyword">const</span> io = <span class="keyword">new</span> IO()</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> port = <span class="number">3000</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 将socket与Koa实例关联</span></span><br><span class="line">io.attach(app)</span><br><span class="line"></span><br><span class="line"><span class="comment">// connection</span></span><br><span class="line">io.on(<span class="string">"connection"</span>, (socket) => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"已连接客户端"</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接收消息</span></span><br><span class="line">io.on(<span class="string">"sendMsg"</span>, (ctx) => {</span><br><span class="line"> <span class="comment">// 将接收到的消息返回给客户端</span></span><br><span class="line"> io.broadcast(<span class="string">"response"</span>, ctx.data)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.listen(port, () => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`service start, open browser locahost:<span class="subst">${port}</span>`</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<h4 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h4><p>在使用过程中,已经确定客户端与服务端连接成功,但是客户端在<code>connect</code>函数中成功输出信息,所以虽然连接成功了,但是客户端的<code>socket</code>函数都没有执行。<br>再次点击发送按钮触发<code>sendMsg</code>,服务端接收到数据,但是客户端接收不到服务端返回的数据。<br>在<code>network</code>中我们看到其实服务端数据已经返回,但是客户端没有执行,所以我猜测是客户端没有拦截到服务端广播回来的数据,也就是没有监听到对应的函数事件。在<code>github</code>上,我在<code>vue-socket.io</code>的<code>issues</code>中看到有人也遇到这个问题,最新版的依赖包都有这个问题,需要将版本降低即可。我们通过降低版本到<code>3.0.7</code>,然后就可以接收到服务端返回的数据了</p>
]]></content>
<categories>
<category>vue</category>
</categories>
<tags>
<tag>vue</tag>
<tag>web-socket</tag>
<tag>vue-socket-io</tag>
</tags>
</entry>
<entry>
<title>vue中使用动画</title>
<url>/2020/07/28/vue%E4%B8%AD%E4%BD%BF%E7%94%A8%E5%8A%A8%E7%94%BB/</url>
<content><![CDATA[<h5 id="使用过渡动画"><a href="#使用过渡动画" class="headerlink" title="使用过渡动画"></a>使用过渡动画</h5><p>1、使用<code>transition</code>标签包裹住动画元素<br>2、在样式中书写</p>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line"><<span class="selector-tag">style</span>></span><br><span class="line"> <span class="selector-class">.name-enter</span> {</span><br><span class="line"> <span class="attribute">opacity</span>: <span class="number">0</span>; <span class="comment">/*默认进入前透明度为0*/</span></span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.name-leave-to</span> {</span><br><span class="line"> <span class="attribute">opacity</span>: <span class="number">0</span>; <span class="comment">/*元素离场的时候透明度改变到多少*/</span></span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.name-enter-active</span> { <span class="comment">/*name为transition标签上的name属性,如果没有设置,则默认为v*/</span></span><br><span class="line"> <span class="attribute">transition-origin</span>: left center;</span><br><span class="line"> <span class="attribute">transition</span>: opacity <span class="number">1s</span>; <span class="comment">/*使用transition属性*/</span></span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.name-leave-active</span> {</span><br><span class="line"> <span class="attribute">transition-origin</span>: left center;</span><br><span class="line"> <span class="attribute">transition</span>: opacity <span class="number">1s</span>;</span><br><span class="line"> }</span><br><span class="line"></style></span><br></pre></td></tr></table></figure>
<h5 id="Vue-使用animate-css"><a href="#Vue-使用animate-css" class="headerlink" title="Vue 使用animate.css"></a>Vue 使用<code>animate.css</code></h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><div id="root"></span><br><span class="line"> <button @click="handleClick">toggle</button></span><br><span class="line"> <transition</span><br><span class="line"> appear /*页面加载完就执行*/</span><br><span class="line"> enter-active-class="animated fadeIn" /*入场动画*/</span><br><span class="line"> enter-leave-class="animated fadeOut" /*离场动画*/</span><br><span class="line"> appear-active-class="animated fadeIn" /*页面加载完就执行*/</span><br><span class="line"> ></span><br><span class="line"> <div v-show="show"></div></span><br><span class="line"> </transition></span><br><span class="line"></div></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line"> var vm = new Vue({</span><br><span class="line"> el: '#root',</span><br><span class="line"> data: {</span><br><span class="line"> show: true</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"> handleClick: function(){</span><br><span class="line"> this.show = !this.show</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"></script></span><br></pre></td></tr></table></figure>
<h5 id="transition标签的使用"><a href="#transition标签的使用" class="headerlink" title="transition标签的使用"></a><code>transition</code>标签的使用</h5><ul>
<li><code>mode</code>:动画效果<ul>
<li><code>in-out</code>:一个元素进入后,另外一个元素消失</li>
<li><code>out-in</code>: 一个元素消失后,另外一个元素进入</li>
</ul>
</li>
<li><code>appear</code>:<code>DOM</code>加载完就执行,配合<code>appear-active-class</code>使用,<code>appear-active-class</code>需要写上对应的类名</li>
<li><code>enter-active-class</code>: 替代<code>.name-enter-active</code>入场动画</li>
<li><code>leave-active-class</code>: 替代<code>.name-leave-active</code>离场动画</li>
<li><code>name</code>: 动画名称,如果定义了在使用的时候就需要以动画名称开头.例如<code>.fade-enter</code>,默认为<code>.v-enter</code></li>
</ul>
<h5 id="transition-group标签的使用"><a href="#transition-group标签的使用" class="headerlink" title="transition-group标签的使用"></a><code>transition-group</code>标签的使用</h5><p>使用<code>transition-group</code>标签是在列表项的时候,相当于在列表项的每一项外层包裹了一个<code>transition</code>标签,使用方式与<code>transition</code>标签相同</p>
]]></content>
<categories>
<category>vue</category>
</categories>
<tags>
<tag>vue</tag>
<tag>动画</tag>
<tag>animate</tag>
</tags>
</entry>
<entry>
<title>在vue中使用DPlayer进行直点播播放</title>
<url>/2020/07/24/%E5%9C%A8vue%E4%B8%AD%E4%BD%BF%E7%94%A8DPlayer%E8%BF%9B%E8%A1%8C%E7%9B%B4%E7%82%B9%E6%92%AD%E6%92%AD%E6%94%BE/</url>
<content><![CDATA[<h5 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h5><p>最近在做督课项目,其中需要对教室的监控进行管理,需要在线监控教室上课情况,因此需要一款可以进行直播的播放器。我们的直播是一个<code>m3u8</code>格式的拉流地址,在满足直播拉流的同时,还需要可以进行点播播放。在每节课上课完成后,监控会将本节课的直播监控存储为点播视频,方便后期对视频进行剪辑、回看。所以我们需要同时满足直播和点播两种业务场景。</p>
<h5 id="技术调研"><a href="#技术调研" class="headerlink" title="技术调研"></a>技术调研</h5><p>在调研后,了解到目前我们可以借助到的开源工具有<a href="https://github.com/surmon-china/vue-video-player" target="_blank" rel="noopener"><code>vue-video-player</code></a>,这款播放器是基于<a href="https://github.com/videojs/video.js" target="_blank" rel="noopener"><code>video.js</code></a>来实现的,能够满足日常的直点播播放,但是在之前使用的过程中,发现在视频加载异常等特殊场景下,<a href="https://github.com/surmon-china/vue-video-player" target="_blank" rel="noopener"><code>vue-video-player</code></a>表现不是很好。而且<a href="https://github.com/videojs/video.js" target="_blank" rel="noopener"><code>video.js</code></a>中集成了<code>flash</code>播放,整个库相对而言也是比较重。在使用层面来讲,文档比较齐全,因为网上相关的文章也不少,可以避免大家再次踩坑。还有一款就是本次相对而言要重点介绍的播放器<a href="http://dplayer.js.org/zh/guide.html#special-thanks" target="_blank" rel="noopener"><code>DPlayer</code></a>,这款播放器的功能丰富,支持直点播、弹幕、水印<code>logo</code>等。缺点就是网上关于它的文章特别少,可以用几乎没有来形容。他有中英文两种文档,在使用过程中很是友好。</p>
<h5 id="踩坑"><a href="#踩坑" class="headerlink" title="踩坑"></a>踩坑</h5><p>在刚开始使用的时候,还没有进入联调,因为使用的只是一个简单的<code>mp4</code>文件进行的测试,播放没有任何问题。但是在后面和后端进行直播调试时,发现没有办法进行直播播放。仔细看了下控制台,提示是没有解析<code>m3u8</code>的库。这个异常提示很是详细,我们定位到了问题出在哪儿就可以进一步的去解决这个问题,其实官网是有对<code>MSE</code>支持,其中就有如何支持<code>HLS</code>播放。</p>
<p>刚开始我没有看到对<code>HLS</code>的使用,然后在加载直播的时候,一直加载失败,看到官网对<code>hls</code>的支持,进行了一翻尝试,完美解决.</p>
<h5 id="项目使用"><a href="#项目使用" class="headerlink" title="项目使用"></a>项目使用</h5><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">// 安装hls</span><br><span class="line">npm install --save hls.js</span><br><span class="line">npm install --save dplayer</span><br></pre></td></tr></table></figure>
<blockquote>
<p>注意:在使用过程中<code>hls</code>要在<code>dplayer</code>之前引入,因为<code>dplayer</code>要解析直播文件是基于<code>hls</code>插件的</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">// player.vue</span><br><span class="line"><template></span><br><span class="line"> <div class="play-container"></span><br><span class="line"> <div ref="dplayer" class="dplayer"></div></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line"></span><br><span class="line">import HLS from "hls.js"</span><br><span class="line">import DPlayer from "dplayer"</span><br><span class="line"></span><br><span class="line">export default {</span><br><span class="line"> name: "Player",</span><br><span class="line"> props: {</span><br><span class="line"> autoplay: { // 自定播放</span><br><span class="line"> type: Boolean,</span><br><span class="line"> default: false</span><br><span class="line"> },</span><br><span class="line"> theme: { // 主题色</span><br><span class="line"> type: "String",</span><br><span class="line"> default: "#04B37C"</span><br><span class="line"> },</span><br><span class="line"> loop: { // 循环播放</span><br><span class="line"> type:Boolean,</span><br><span class="line"> default: true</span><br><span class="line"> },</span><br><span class="line"> lang: { // 语言</span><br><span class="line"> type: String,</span><br><span class="line"> default: "zh"</span><br><span class="line"> },</span><br><span class="line"> hotkey: { // 热键</span><br><span class="line"> type: Boolean,</span><br><span class="line"> default: true</span><br><span class="line"> },</span><br><span class="line"> preload: { // 预加载</span><br><span class="line"> type: Boolean,</span><br><span class="line"> default: "auto"</span><br><span class="line"> },</span><br><span class="line"> contextmenu: { // 自定义右键</span><br><span class="line"> type: Array</span><br><span class="line"> },</span><br><span class="line"> logo: { // 水印logo</span><br><span class="line"> type: String</span><br><span class="line"> },</span><br><span class="line"> live: { // 是否为直播</span><br><span class="line"> type: Boolean,</span><br><span class="line"> default: false</span><br><span class="line"> },</span><br><span class="line"> video: {</span><br><span class="line"> type: Object,</span><br><span class="line"> required: true,</span><br><span class="line"> validator(value) {</span><br><span class="line"> return typeof value.url === 'string'</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> data() {</span><br><span class="line"> return {</span><br><span class="line"> dp: null</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> mounted() {</span><br><span class="line"> this.initPlayer()</span><br><span class="line"> },</span><br><span class="line"> methods: {</span><br><span class="line"> // 初始化播放器</span><br><span class="line"> initPlayer() {</span><br><span class="line"> if (!this.video || !this.video.url) {</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> this.dp = new DPlayer({</span><br><span class="line"> element: this.$refs.dplayer,</span><br><span class="line"> autoplay: this.autoplay,</span><br><span class="line"> theme: this.theme,</span><br><span class="line"> loop: this.loop,</span><br><span class="line"> lang: this.lang,</span><br><span class="line"> screenshot: this.screenshot,</span><br><span class="line"> hotkey: this.hotkey,</span><br><span class="line"> preload: this.preload,</span><br><span class="line"> logo: this.logo,</span><br><span class="line"> subtitle: this.subtitle,</span><br><span class="line"> live: this.live,</span><br><span class="line"> video: {</span><br><span class="line"> url: this.video.url,</span><br><span class="line"> pic: this.video.pic,</span><br><span class="line"> type: "customHls",</span><br><span class="line"> customType: {</span><br><span class="line"> customHls: function(video) {</span><br><span class="line"> const hls = new Hls();</span><br><span class="line"> hls.loadSource(video.src);</span><br><span class="line"> hls.attachMedia(video);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> this.dp.on("play", () => {</span><br><span class="line"> this.$emit("play");</span><br><span class="line"> });</span><br><span class="line"> this.dp.on("pause", () => {</span><br><span class="line"> this.$emit("pause");</span><br><span class="line"> });</span><br><span class="line"> this.dp.on("canplay", () => {</span><br><span class="line"> this.$emit("canplay");</span><br><span class="line"> });</span><br><span class="line"> this.dp.on("playing", () => {</span><br><span class="line"> this.$emit("playing");</span><br><span class="line"> });</span><br><span class="line"> this.dp.on("ended", () => {</span><br><span class="line"> this.$emit("ended");</span><br><span class="line"> });</span><br><span class="line"> this.dp.on("error", () => {</span><br><span class="line"> this.$emit("error");</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></script></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>Vue</category>
</categories>
<tags>
<tag>Vue</tag>
<tag>DPlayer</tag>
<tag>直播</tag>
<tag>点播</tag>
</tags>
</entry>
<entry>
<title>实现Virtual_DOM</title>
<url>/2020/03/24/%E5%AE%9E%E7%8E%B0Virtual-DOM/</url>
<content><![CDATA[<h5 id="为什么要使用-Virtual-DOM"><a href="#为什么要使用-Virtual-DOM" class="headerlink" title="为什么要使用 Virtual DOM"></a>为什么要使用 Virtual DOM</h5><p>如果我们要对页面多条数据进行操作,我们就需要来操作 DOM,如果数据量太大,DOM 操作也就频繁,如果应用程序越来越复杂,更新页面的 DOM 操作也就越来越多了,应用程序会变得非常难维护。</p>
<p>这时人们就想到了,可以对需要改变或更新的数据添加状态,然后将视图(view)跟状态进行绑定,如果数据状态发生改变,这样当我们改变数据状态时,视图也就自动跟着变化。这就是人们想出的 MVVM 模式,只要在模板中生命视图组件是和什么状态进行绑定的,双向绑定引擎就会在状态更新的时候自动更新视图</p>
<p>MVVM 可以降低我们维护状态到视图的复杂程度,大幅度减少了代码中的视图更新逻辑。还有一个非常直观的办法,一旦状态发生改变,就用模板引擎重新渲染整个视图,然后用新的视图替换掉旧的视图。相当于<code>remove</code>掉旧的 DOM 节点,在 JS 中将重新渲染的页面<code>innerHTML</code>到页面上,但是这样会很慢,因为即使一个小小的状态变更都需要重新构建 DOM,性能降低;(Backbone 就是这么去做的);但是对于大型视图,全局应用状态变更时,需要更新的局部视图就会很多,这样的做法不可取</p>
<p><code>Virtual DOM</code>就是这么做的,只是加了一些特别的步骤避免了整棵 DOM 树变更</p>
<p>我们可以将 DOM 对象用原生的 JavaScript 对象来出来,因为原生 JavaScript 处理速度非常快</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> element = {</span><br><span class="line"> tagName: <span class="string">"ul"</span>, <span class="comment">//节点标签名</span></span><br><span class="line"> props: {</span><br><span class="line"> <span class="comment">//DOM的属性,用一个对象存储键值对</span></span><br><span class="line"> id: <span class="string">"list"</span>,</span><br><span class="line"> },</span><br><span class="line"> children: [</span><br><span class="line"> <span class="comment">//该节点的子节点</span></span><br><span class="line"> { <span class="attr">tagName</span>: <span class="string">"li"</span>, <span class="attr">props</span>: { <span class="attr">class</span>: <span class="string">"item"</span> }, <span class="attr">children</span>: [<span class="string">"item 1"</span>] },</span><br><span class="line"> { <span class="attr">tagName</span>: <span class="string">"li"</span>, <span class="attr">props</span>: { <span class="attr">class</span>: <span class="string">"item"</span> }, <span class="attr">children</span>: [<span class="string">"item 2"</span>] },</span><br><span class="line"> { <span class="attr">tagName</span>: <span class="string">"li"</span>, <span class="attr">props</span>: { <span class="attr">class</span>: <span class="string">"item"</span> }, <span class="attr">children</span>: [<span class="string">"item 3"</span>] },</span><br><span class="line"> ],</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面对应的 HTML 写法是:</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ul</span> <span class="attr">id</span>=<span class="string">"list"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"item"</span>></span>Item 1<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"item"</span>></span>Item 2<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"item"</span>></span>Item 3<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"><span class="tag"></<span class="name">ul</span>></span></span><br></pre></td></tr></table></figure>
<p>这样我们就可以使用 JavaScript 对象来表示一颗 DOM 树的信息,也可以使用 JavaScript 对象来构建一颗真正的 DOM 树</p>
<p>但是当状态变更的时候,重新渲染这个 JavaScript 对象结构,也没什么用,因为真正的页面其实没有改变</p>
<p>但是可以用新渲染的对象树去和旧的树进行对比,记录两棵树的差异,记录下来的不同就是我们需要对页面真正的 DOM 操作,然后把他们应用到真正的 DOM 树上,页面也就变更了。这样就可以做到:视图的结构确实时整个重新渲染,但是最后操作的 DOM 的时候只变更不同的地方</p>
<p>这就是所谓的 Virtual DOM 算法。包括下面几个步骤:</p>
<ul>
<li>1、用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中</li>
<li>2、当状态变更的时候,重新构造一颗新的对象树。然后用心得树和旧的树进行比较,记录两棵树差异</li>
<li>3、把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了<br>Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)</li>
</ul>
<h4 id="4-算法实现"><a href="#4-算法实现" class="headerlink" title="4 算法实现"></a>4 算法实现</h4><h5 id="4-1-步骤一:用-JS-对象模拟-DOM-树"><a href="#4-1-步骤一:用-JS-对象模拟-DOM-树" class="headerlink" title="4.1 步骤一:用 JS 对象模拟 DOM 树"></a>4.1 步骤一:用 JS 对象模拟 DOM 树</h5><p>用 JavaScript 来表示一个 DOM 节点是很简单的事情,你只需要记录它的节点类型、属性,还有子节点:</p>
<p>Element.js</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Element</span>(<span class="params">tagName, props, children</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.tagName = tagName</span><br><span class="line"> <span class="keyword">this</span>.props = props || {}</span><br><span class="line"> <span class="keyword">this</span>.children = children || []</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params">tagName, props, children</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Element(tagName, props, children)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>例如上面的 DOM 结构就可以简单的表示:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> el = <span class="built_in">require</span>(<span class="string">"./element"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> ul = el(<span class="string">"ul"</span>, { <span class="attr">id</span>: <span class="string">"list"</span> }, [</span><br><span class="line"> el(<span class="string">"li"</span>, { <span class="attr">class</span>: <span class="string">"item"</span> }, [<span class="string">"Item 1"</span>]),</span><br><span class="line"> el(<span class="string">"li"</span>, { <span class="attr">class</span>: <span class="string">"item"</span> }, [<span class="string">"Item 2"</span>]),</span><br><span class="line"> el(<span class="string">"li"</span>, { <span class="attr">class</span>: <span class="string">"item"</span> }, [<span class="string">"Item 3"</span>]),</span><br><span class="line">])</span><br></pre></td></tr></table></figure>
<p>现在<code>ul</code>只是一个 JavaScript 对象表示的 DOM 结构,页面上并没有这个结构。我们可以根据这个<code>ul</code>构建真正的<code><ul</code>>:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">Element.prototype.render = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> el = <span class="built_in">document</span>.createElement(<span class="keyword">this</span>.tagName) <span class="comment">//根据tagName构建</span></span><br><span class="line"> <span class="keyword">var</span> props = <span class="keyword">this</span>.props</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> propName <span class="keyword">in</span> props) { <span class="comment">//设置节点的DOM属性</span></span><br><span class="line"> <span class="keyword">var</span> propValue = props[propName]</span><br><span class="line"> el.setAttribute(propName, propValue)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> children = <span class="keyword">this</span>.children || []</span><br><span class="line"></span><br><span class="line"> children.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">child</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> childEl = (child <span class="keyword">instanceof</span> Element)</span><br><span class="line"> ? child.render() <span class="comment">//如果子节点也是虚拟DOM,递归构建DOM节点</span></span><br><span class="line"> : doeument.createTextNode(child) <span class="comment">//如果字符串,只构建文本节点</span></span><br><span class="line"> el.appendChild(childEl)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> el</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>render</code>方法会根据<code>tagName</code>构建一个真正的 DOM 节点,然后设置这个节点的属性,最后递归把自己的子节点也构建起来。所以只需要:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> ulRoot = ul.render()</span><br><span class="line"><span class="built_in">document</span>.body.appendChild(ulRoot)</span><br></pre></td></tr></table></figure>
<p>上面的<code>ulRoot</code>是真正的 DOM 节点,把它塞入文档中,这样<code>body</code>里面就有了真正的<code><ul></code>的 DOM 结构:</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">ul</span> <span class="attr">id</span>=<span class="string">"list"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"item"</span>></span>Item 1<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"item"</span>></span>Item 2<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span> <span class="attr">class</span>=<span class="string">"item"</span>></span>Item 3<span class="tag"></<span class="name">li</span>></span></span><br><span class="line"><span class="tag"></<span class="name">ul</span>></span></span><br></pre></td></tr></table></figure>
<p><a href=""https://github.com/livoras/simple-virtual-dom/blob/master/lib/element.js"">完整代码<code>element</code></a></p>
<h5 id="4-2-步骤二:比较两颗虚拟-DOM-树的差异"><a href="#4-2-步骤二:比较两颗虚拟-DOM-树的差异" class="headerlink" title="4.2 步骤二:比较两颗虚拟 DOM 树的差异"></a>4.2 步骤二:比较两颗虚拟 DOM 树的差异</h5><p>比较两颗 DOM 树的差异是 Virtual DOM 算法最核心的部分,这也是所谓的 Virtual DOM 的 diff 算法。两个树的完全的 diff 算法是一个时间复杂度为 O(n^3)的问题。但是在前端当中,你很少会跨层级的移动 DOM 元素。所以 Virtual DOM 只会对同一个层级的元素进行对比:</p>
<p>上面的<code>div</code>只会和统一层级的<code>div</code>对比,第二层级的只会跟第二层级对比。这样算法复杂度就可以达到 O(n).</p>
<h6 id="4-2-1-深度优先遍历,记录差异"><a href="#4-2-1-深度优先遍历,记录差异" class="headerlink" title="4.2.1 深度优先遍历,记录差异"></a>4.2.1 深度优先遍历,记录差异</h6><p>在实际的代码中,会对新旧两棵树进行一个深度优先的遍历,这样每个节点都会有一个唯一的标记:</p>
<p>在深度优先遍历的时候,没遍历到一个节点就把该节点和新的树进行对比。如果有差异的话就记录到一个对象里面</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//diff函数,对比两棵树</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">diff</span>(<span class="params">olTree, newTree</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> index = <span class="number">0</span> <span class="comment">//当前节点的标志</span></span><br><span class="line"> <span class="keyword">var</span> patches = {} <span class="comment">//用来记录每个节点差异的对象</span></span><br><span class="line"> dfsWalk(oldTree, newTree, index, paches)</span><br><span class="line"> <span class="keyword">return</span> patches</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//对两棵树进行深度优先遍历</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">dfsWalk</span>(<span class="params">oldNode, newNode, index, patches</span>) </span>{</span><br><span class="line"> <span class="comment">//对比oldNode和newNode的不同,记录下来</span></span><br><span class="line"> patches[index] = [...]</span><br><span class="line"></span><br><span class="line"> diffChildren(oldNode.children, newNode.children, index, patches)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//遍历子节点</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">diffChildren</span> (<span class="params">oldChildren, newChildren, index, patches</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> leftNode = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">var</span> currentNodeIndex = index</span><br><span class="line"> oldChildren.forEach(<span class="function"><span class="keyword">function</span>(<span class="params">child, i</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> newChild = newChildren[i]</span><br><span class="line"> currentNodeIndex = (leftNode && leftNode.count) <span class="comment">//计算节点的标识</span></span><br><span class="line"> ? currentNodeIndex + leftNode.count + <span class="number">1</span></span><br><span class="line"> : currentNodeIndex + <span class="number">1</span></span><br><span class="line"> dfsWalk(child, newChild, currentNodeIndex, patches) <span class="comment">//深度遍历子节点</span></span><br><span class="line"> leftNode = child</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h5 id="4-2-2-差异类型"><a href="#4-2-2-差异类型" class="headerlink" title="4.2.2 差异类型"></a>4.2.2 差异类型</h5><p>上面说的节点的差异是指对 DOM 的操作可能会:</p>
<ul>
<li>替换掉原来的节点,例如把上面的<code>div</code>换乘<code>section</code></li>
<li>移动、删除、新增子节点</li>
<li>修改节点属性</li>
<li>文本内容可能会改变</li>
</ul>
<p>所以我们定义了几种差异类型:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> REPLACE = <span class="number">0</span></span><br><span class="line"><span class="keyword">var</span> REORDER = <span class="number">1</span></span><br><span class="line"><span class="keyword">var</span> PROPS = <span class="number">2</span></span><br><span class="line"><span class="keyword">var</span> TEXT = <span class="number">3</span></span><br></pre></td></tr></table></figure>
<p>对于节点替换,很简单。判断新旧节点的<code>tagName</code>和是不是一样的,如果不一样说明需要替换掉。如<code>div</code>换成<code>section</code>:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">patches[<span class="number">0</span>] = [</span><br><span class="line"> {</span><br><span class="line"> type: REPLACE,</span><br><span class="line"> node: newNode,</span><br><span class="line"> },</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>如果给<code>div</code>新增了属性<code>id</code>为<code>container</code>,就记录下:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">patches[<span class="number">0</span>] = [</span><br><span class="line"> {</span><br><span class="line"> type: REPLACE,</span><br><span class="line"> node: newNode,</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> type: PROPS,</span><br><span class="line"> props: {</span><br><span class="line"> id: <span class="string">"container"</span>,</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>如果是文本节点:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">patches[<span class="number">2</span>] = [</span><br><span class="line"> {</span><br><span class="line"> type: TEXT,</span><br><span class="line"> content: <span class="string">"Virtual DOM2"</span>,</span><br><span class="line"> },</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>如果我们只是将几个 DOM 元素的位置进行了调换,那么又该如何判断呢?</p>
<p>如果只改变几个 DOM 元素的位置,去使用替换的话,那么 DOM 开销就非常大。而实际我们只需要通过移动节点来完成,这个就涉及到两个列表的对比算法了</p>
<h5 id="4-2-3-列表对比算法"><a href="#4-2-3-列表对比算法" class="headerlink" title="4.2.3 列表对比算法"></a>4.2.3 列表对比算法</h5><p>例如属性对比算法:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">diffProps</span>(<span class="params">oldNode, newNode</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> count = <span class="number">0</span></span><br><span class="line"> <span class="keyword">var</span> oldProps = oldNode.props</span><br><span class="line"> <span class="keyword">var</span> newProps = newNode.props</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> key, value</span><br><span class="line"> <span class="keyword">var</span> propsPatches = {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">//查找不同属性</span></span><br><span class="line"> <span class="keyword">for</span> (key <span class="keyword">in</span> oldProps) {</span><br><span class="line"> value = oldProps[key]</span><br><span class="line"> <span class="keyword">if</span> (newProps[key] != value) {</span><br><span class="line"> count++</span><br><span class="line"> propsPatches[key] = newProps[key]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//查找新属性</span></span><br><span class="line"> <span class="keyword">for</span> (key <span class="keyword">in</span> newProps) {</span><br><span class="line"> value = newProps[key]</span><br><span class="line"> <span class="keyword">if</span> (!oldProps.hasOwnProperty(key)) {</span><br><span class="line"> count++</span><br><span class="line"> propsPatches[key] = newProps[key]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//如果全部相同</span></span><br><span class="line"> <span class="keyword">if</span> (count === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> propsPatches</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>用<code>count</code>来记录变化的次数,首先查看不同属性,通过遍历旧的属性对象,如果新的属性对象中的不同,则<code>count++</code>并且将新的属性以及值添加到记录变化的属性对象中。然后再查找新增的属性,遍历新属性对象,如果在旧属性对象中没有新属性,则<code>count++</code>,并且将新属性以及值添加到记录变化的属性对象中</p>
<h5 id="4-3-步骤三:-把差异应用到真正的-DOM-树上"><a href="#4-3-步骤三:-把差异应用到真正的-DOM-树上" class="headerlink" title="4.3 步骤三: 把差异应用到真正的 DOM 树上"></a>4.3 步骤三: 把差异应用到真正的 DOM 树上</h5><p>因为步骤一构建出来的 JS 对象树和<code>render()</code>出来的真正的 DOM 树信息、结构是一样的。所以我们可以先对对象书进行深度优先的遍历,遍历的时候从步骤二生成的<code>patches</code>对象中找出当前遍历的节点差异,然后进行 DOM 操作</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">patch</span> (<span class="params">nodem patches</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> walker = {<span class="attr">index</span>: <span class="number">0</span>}</span><br><span class="line"> dfsWalk(node, walker, patches)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">dfsWalk</span> (<span class="params">node, walker, patches</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> currentPatches = patches[walker.index] <span class="comment">//从patches拿出当前节点的差异</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> len = node.childNodes</span><br><span class="line"> ? node.childNodes.length</span><br><span class="line"> : <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < len; i++) { <span class="comment">//深度遍历子节点</span></span><br><span class="line"> <span class="keyword">var</span> child = node.childNodes[i]</span><br><span class="line"> walker.index++</span><br><span class="line"> dfsWalk(child, walker, patches)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(currentPatches) {</span><br><span class="line"> applyPatches(node, currentPatches) <span class="comment">//对当前节点进行DOM操作</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>applyPatches, 根据不同类型的差异对当前节点进行 DOM 操作:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">applyPatches</span> (<span class="params">node, currentPatches</span>) </span>{</span><br><span class="line"> currentPatchs.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">currentPatch</span>)) </span>{</span><br><span class="line"> <span class="keyword">switch</span> (currentPatch.type) {</span><br><span class="line"> <span class="keyword">case</span> REPLACE:</span><br><span class="line"> node.parentNode.replaceChild(currentPatch.node.render(), node)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="keyword">case</span> REORDER:</span><br><span class="line"> reorderChildren(node, currentPatch.moves)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="keyword">case</span> PROPS:</span><br><span class="line"> setProps(node, currentPath.props)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="keyword">case</span> TEXT:</span><br><span class="line"> node.textContent = currentPatch.content</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> defualt:</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Unknown patch type '</span> + currentPatch.type)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><a href="https://github.com/livoras/simple-virtual-dom/blob/master/lib/patch.js" target="_blank" rel="noopener">源代码</a></p>
<p>如果当前对象树有子元素,则对子元素进行深度优先遍历,这就是为什么在写 React 组件的时候,需要最外层包裹一个 DIV。不然,在深度遍历的时候只能遍历到第一个元素。然后通过<code>aplyPatches</code>函数将发生变化部分的 DOM 对象应用到实际的 DOM 元素中,这样就大大减少了对浏览器中实际 DOM 的操作。</p>
]]></content>
<categories>
<category>react</category>
</categories>
<tags>
<tag>react</tag>
<tag>Virtual-DOM</tag>
</tags>
</entry>
</search>