-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
547 lines (446 loc) · 165 KB
/
index.html
File metadata and controls
547 lines (446 loc) · 165 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.8.0">
<meta charset="utf-8">
<title>Hexo</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:type" content="website">
<meta property="og:title" content="Hexo">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:locale" content="default">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Hexo">
<link rel="alternate" href="/atom.xml" title="Hexo" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/style.css">
</head>
</html>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/" id="logo">Hexo</a>
</h1>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
</nav>
<nav id="sub-nav">
<a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
<a id="nav-search-btn" class="nav-icon" title="Search"></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="http://yoursite.com"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-ast-有魅力的绿巨人-第二回合-丁岳" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/05/29/ast-有魅力的绿巨人-第二回合-丁岳/" class="article-date">
<time datetime="2019-05-29T02:55:14.351Z" itemprop="datePublished">2019-05-29</time>
</a>
</div>
<div class="article-inner">
<div class="article-entry" itemprop="articleBody">
<h2 id="AST-抽象语法树无处不在-第二回合"><a href="#AST-抽象语法树无处不在-第二回合" class="headerlink" title="AST 抽象语法树无处不在-第二回合"></a>AST 抽象语法树无处不在-第二回合</h2><h2 id="一,Export-Default-到-Component-构造器的转换"><a href="#一,Export-Default-到-Component-构造器的转换" class="headerlink" title="一,Export Default 到 Component 构造器的转换"></a>一,Export Default 到 Component 构造器的转换</h2><p>接着上回(AST 抽象语法树无处不在-第一回合)我们继续 VUE 组件转换为小程序组件中的 JavaScript 部分的转换。<br>首先我们看一下要转换前后的语法树与代码如下(明确转换目标):</p>
<h6 id="转换之前的-AST-树与代码"><a href="#转换之前的-AST-树与代码" class="headerlink" title="转换之前的 AST 树与代码"></a>转换之前的 AST 树与代码</h6><p><img src="http://img11.360buyimg.com/uba/jfs/t29581/123/1031219550/7088/28f16ea1/5c05dddfN076efa20.png"></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {<span class="comment">// VUE 组件的惯用写法用于导出对象模块</span></span><br><span class="line"> data(){</span><br><span class="line"> },</span><br><span class="line"> methods:{</span><br><span class="line"> },</span><br><span class="line"> props:{</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h6 id="转换之后的-AST-树与代码"><a href="#转换之后的-AST-树与代码" class="headerlink" title="转换之后的 AST 树与代码"></a>转换之后的 AST 树与代码</h6><p><img src="http://img11.360buyimg.com/uba/jfs/t26155/227/2594893184/9762/8d47d607/5c05dddfNe823c12a.png"></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">components({<span class="comment">//小程序组件的构造器</span></span><br><span class="line"> data(){</span><br><span class="line"> },</span><br><span class="line"> methods:{</span><br><span class="line"> },</span><br><span class="line"> props:{</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>通过以上转换之前和转换之后代码和 AST 的对比我们明确了转换目标就是 ExportDefault 到 Component构造器的转换,下面看一下我们是如何处理的:</p>
<h6 id="我们做了什么(在转换中进入到-ExportDefault-中做对应的处理)"><a href="#我们做了什么(在转换中进入到-ExportDefault-中做对应的处理)" class="headerlink" title="我们做了什么(在转换中进入到 ExportDefault 中做对应的处理):"></a>我们做了什么(在转换中进入到 ExportDefault 中做对应的处理):</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ExportDefault 到 Component构造器的转换</span></span><br><span class="line">ExportDefaultDeclaration(path) {</span><br><span class="line"><span class="comment">//创建 CallExpression Component({})</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">insertBeforeFn</span>(<span class="params">path</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> objectExpression = t.objectExpression(propertiesAST);</span><br><span class="line"> test = t.expressionStatement(</span><br><span class="line"> t.callExpression(<span class="comment">//创建名为 Compontents 的调用表达式,参数为 objectExpression</span></span><br><span class="line"> t.identifier(<span class="string">"Compontents"</span>),[</span><br><span class="line"> objectExpression</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="built_in">console</span>.log(<span class="string">"test"</span>,test)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (path.node.type === <span class="string">"ExportDefaultDeclaration"</span>) {</span><br><span class="line"> <span class="keyword">if</span> (path.node.declaration.properties) {</span><br><span class="line"> <span class="comment">//提取属性并存储</span></span><br><span class="line"> propertiesAST = path.node.declaration.properties;</span><br><span class="line"> <span class="comment">//创建 AST 包裹对象</span></span><br><span class="line"> insertBeforeFn(path); </span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//得到我们最终的转换结果</span></span><br><span class="line"> <span class="built_in">console</span>.log(generate(test, {}, code).code);</span><br></pre></td></tr></table></figure>
<p>对于 ExportDefault => Component 构造器转换还有一种转换思路 下面我们看一下:<br><br>[1] 第一种思路是先提取 ExportDefault 内部所有节点的 AST ,并做处理,然后创建Component构造器,插入提取处理后的 AST,得到最终的 AST</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//propertiesAST 这个就是我们拿到的 AST,然后在对应的分支内做对应的处理 以下分别为 data,methods,props,其他的钩子同样处理即可</span></span><br><span class="line">propertiesAST.map(<span class="function">(<span class="params">item, index</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (item.type === <span class="string">"ObjectProperty"</span>) {</span><br><span class="line"> <span class="comment">//props 替换为 properties</span></span><br><span class="line"> <span class="keyword">if</span> (item.key.name === <span class="string">"props"</span>) {</span><br><span class="line"> item.key.name = <span class="string">"properties"</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (item.type === <span class="string">"ObjectMethod"</span>) {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"other node type"</span>, item.type);</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>[2] 第二种思路呢,就是我们上面展示的这种,不过有一个关键的地方要注意一下:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//我把 ExportDefaultDeclaration 的处理放到最后来执行,拿到 AST 首先进行转换。然后在创建得到新的小程序组件JS部分的 AST 即可</span></span><br><span class="line">traverse(ast, {</span><br><span class="line"> enter(path) {},</span><br><span class="line"> ObjectProperty(path) {},</span><br><span class="line"> ObjectMethod(path) {},</span><br><span class="line"> <span class="comment">//......其他</span></span><br><span class="line"> ExportDefaultDeclaration(path) {</span><br><span class="line"> <span class="comment">//参见上方</span></span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>如果你想在 AST 开始处与结尾处插入,请参考:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">path.insertBefore(</span><br><span class="line"> t.expressionStatement(t.stringLiteral(<span class="string">"start.."</span>))</span><br><span class="line">);</span><br><span class="line">path.insertAfter(</span><br><span class="line"> t.expressionStatement(t.stringLiteral(<span class="string">"end.."</span>))</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<h2 id="二,VUE-组件转换为小程序组件中的-Data-部分的处理:"><a href="#二,VUE-组件转换为小程序组件中的-Data-部分的处理:" class="headerlink" title="二,VUE 组件转换为小程序组件中的 Data 部分的处理:"></a>二,VUE 组件转换为小程序组件中的 Data 部分的处理:</h2><p>关于 Data 部分的处理实际上就是:函数表达式转换为对象表达式 (FunctionExpression 转换为 ObjectExpression)</p>
<h6 id="转换之前的-JavaScript-代码"><a href="#转换之前的-JavaScript-代码" class="headerlink" title="转换之前的 JavaScript 代码"></a>转换之前的 JavaScript 代码</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> data(){<span class="comment">//函数表达式</span></span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> num: <span class="number">10000</span>,</span><br><span class="line"> arr: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>],</span><br><span class="line"> obj: {</span><br><span class="line"> d1: <span class="string">"val1"</span>,</span><br><span class="line"> d2: <span class="string">"val2"</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>
<h6 id="处理后我们得到的"><a href="#处理后我们得到的" class="headerlink" title="处理后我们得到的"></a>处理后我们得到的</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> data: {<span class="comment">//对象表达式</span></span><br><span class="line"> num: <span class="number">10000</span>,</span><br><span class="line"> arr: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>],</span><br><span class="line"> obj: {</span><br><span class="line"> d1: <span class="string">"val1"</span>,</span><br><span class="line"> d2: <span class="string">"val2"</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>
<h6 id="转换前后-AST-树对比图明确转换目标:"><a href="#转换前后-AST-树对比图明确转换目标:" class="headerlink" title="转换前后 AST 树对比图明确转换目标:"></a>转换前后 AST 树对比图明确转换目标:</h6><p><img src="http://img11.360buyimg.com/uba/jfs/t28720/269/1084213324/12861/fb20d3a5/5c05e360N576b3290.png"><br><br><img src="http://img30.360buyimg.com/uba/jfs/t29215/312/1056174776/12311/67b44b9f/5c05e384Nb98eff0b.png"><br></p>
<h6 id="我们对-JavaScript-动了什么手脚-亦可封装成babel插件"><a href="#我们对-JavaScript-动了什么手脚-亦可封装成babel插件" class="headerlink" title="我们对 JavaScript 动了什么手脚(亦可封装成babel插件):"></a>我们对 JavaScript 动了什么手脚(亦可封装成babel插件):</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ast = babylon.parse(code, {</span><br><span class="line"> sourceType: <span class="string">"module"</span>,</span><br><span class="line"> plugins: [<span class="string">"flow"</span>]</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//AST 转换node、nodetype很关键</span></span><br><span class="line">traverse(ast, {</span><br><span class="line"> enter(path) {</span><br><span class="line"> <span class="comment">//打印出node.type</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"enter: "</span> + path.node.type);</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<h6 id="我们的转换部分都尽量在一个-Traverse-中处理,减少-AST-树遍历的性能消耗"><a href="#我们的转换部分都尽量在一个-Traverse-中处理,减少-AST-树遍历的性能消耗" class="headerlink" title="我们的转换部分都尽量在一个 Traverse 中处理,减少 AST 树遍历的性能消耗"></a>我们的转换部分都尽量在一个 Traverse 中处理,减少 AST 树遍历的性能消耗</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Data 函数表达式 转换为 Object</span></span><br><span class="line">ObjectMethod(path) {</span><br><span class="line"> <span class="comment">// console.log("path.node ",path.node )// data, add, textFn</span></span><br><span class="line"> <span class="keyword">if</span> (path.node.key.name === <span class="string">"data"</span>) {</span><br><span class="line"> <span class="comment">// 获取第一级的 BlockStatement,也就是 Data 函数体</span></span><br><span class="line"> path.traverse({</span><br><span class="line"> BlockStatement(p) {</span><br><span class="line"> <span class="comment">//从 Data 中提取数据属性</span></span><br><span class="line"> datas = p.node.body[<span class="number">0</span>].argument.properties;</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">const</span> objectExpression = t.objectExpression(datas);</span><br><span class="line"> <span class="comment">//创建 Data 对象并赋值</span></span><br><span class="line"> <span class="keyword">const</span> dataProperty = t.objectProperty(</span><br><span class="line"> t.identifier(<span class="string">"data"</span>),</span><br><span class="line"> objectExpression</span><br><span class="line"> );</span><br><span class="line"> <span class="comment">//插入到原 Data 函数下方</span></span><br><span class="line"> path.insertAfter(dataProperty);</span><br><span class="line"> <span class="comment">//删除原 Data 函数节点</span></span><br><span class="line"> path.remove();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="三,CSS-部分的处理:"><a href="#三,CSS-部分的处理:" class="headerlink" title="三,CSS 部分的处理:"></a>三,CSS 部分的处理:</h2><p>那 CSS 我们也是必须要处理的一部分,let try</p>
<h6 id="以下是我们要处理的css样本"><a href="#以下是我们要处理的css样本" class="headerlink" title="以下是我们要处理的css样本"></a>以下是我们要处理的css样本</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">const code = `</span><br><span class="line"> <span class="selector-class">.text-ok</span>{</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#e4393c</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.nut-popup-close</span>{</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">50px</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">120px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">26px</span>;</span><br><span class="line"> }`;</span><br></pre></td></tr></table></figure>
<h6 id="处理后我们得到的-1"><a href="#处理后我们得到的-1" class="headerlink" title="处理后我们得到的"></a>处理后我们得到的</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.text-ok</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">351</span>rpx;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#e4393c</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.nut-popup-close</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">117</span>rpx;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">280.79</span>rpx;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">468</span>rpx;</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">60.84</span>rpx;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>通过前后代码的对比,我们看到了单位尺寸的转换(比如:top: 50px; 转换为 top: 117rpx;)。<br>单位的转换( px 转为了 rpx )</p>
<h6 id="CSS-又做了哪些处理呢?"><a href="#CSS-又做了哪些处理呢?" class="headerlink" title="CSS 又做了哪些处理呢?"></a>CSS 又做了哪些处理呢?</h6><p>同样也有不少的 CSS Code Parsers 供我们选择 Cssom ,CssTree等等,<br>我们拿 Cssom 来实现上方css代码的一个简单的转换。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ast = csstree.parse(code);</span><br><span class="line"> csstree.walk(ast, <span class="function"><span class="keyword">function</span>(<span class="params">node</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">typeof</span> node.value == <span class="string">"string"</span> && <span class="built_in">isNaN</span>(node.value) != <span class="literal">true</span>){</span><br><span class="line"> <span class="keyword">let</span> newVal = <span class="built_in">Math</span>.floor((node.value*<span class="number">2.34</span>) * <span class="number">100</span>) / <span class="number">100</span>;<span class="comment">//转换比例这个根据情况设置即可</span></span><br><span class="line"> <span class="keyword">if</span>(node.type === <span class="string">"Dimension"</span>){<span class="comment">//得到要转换的数字尺寸</span></span><br><span class="line"> node.value = newVal;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(node.unit === <span class="string">"px"</span>){<span class="comment">//单位的处理</span></span><br><span class="line"> node.unit = <span class="string">"rpx"</span></span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="built_in">console</span>.log(csstree.generate(ast));</span><br></pre></td></tr></table></figure>
<p>当然这只是一个 demo,实际项目中使用还的根据项目的实际情况出发,SCSS,LESS等等的转换与考虑不同的处理场景哦!</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>希望,本文对大家有所帮助,在技术探索的路上,我们一往无前, Paladin 精神永存!<br>感谢大家的耐心阅读,也欢迎大家关注 全栈探索公众号,每周都会有技术好文推出!<br>发现个好东东,如下:</p>
<h6 id="将-JavaScript-代码转化生成-SVG-流程图-js2flowchart-4-5-k-stars-在-GitHub"><a href="#将-JavaScript-代码转化生成-SVG-流程图-js2flowchart-4-5-k-stars-在-GitHub" class="headerlink" title="将 JavaScript 代码转化生成 SVG 流程图 js2flowchart( 4.5 k stars 在 GitHub )"></a>将 JavaScript 代码转化生成 SVG 流程图 js2flowchart( 4.5 k stars 在 GitHub )</h6><p>当你拥有 AST 时,可以做任何你想要做的事。把AST转回成字符串代码并不是必要的,你可以通过它画一个流程图,或者其它你想要的东西。<br>js2flowchart使用场景是什么呢?通过流程图,你可以解释你的代码,或者给你代码写文档;通过可视化的解释学习其他人的代码;通过简单的js语法,为每个处理过程简单的描述创建流程图。<br>马上用最简单的方式尝试一下吧,去线上编辑看看 <a href="https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart/" target="_blank" rel="noopener">js-code-to-svg-flowchart</a> [9]。<br>此处有必要附上截图一张。</p>
<p><img src="http://img10.360buyimg.com/uba/jfs/t28051/66/739453023/25363/43b62be0/5bfd0d0aNb5f485c5.png"></p>
<h2 id="扩展阅读"><a href="#扩展阅读" class="headerlink" title="扩展阅读"></a>扩展阅读</h2><p>[1] <a href="https://astexplorer.net/" target="_blank" rel="noopener">https://astexplorer.net/</a></p>
<p>[2] <a href="https://babeljs.io/docs/en/next/babel-types.html#objectexpression" target="_blank" rel="noopener">https://babeljs.io/docs/en/next/babel-types.html#objectexpression</a> </p>
<p>[3] <a href="https://github.com/babel/babel/tree/6.x/packages/babel-types" target="_blank" rel="noopener">https://github.com/babel/babel/tree/6.x/packages/babel-types</a></p>
<p>[4] <a href="http://esprima.org/demo/parse.html#" target="_blank" rel="noopener">http://esprima.org/demo/parse.html#</a></p>
<p>[5] <a href="https://segmentfault.com/a/1190000014178462?utm_source=tag-newest" target="_blank" rel="noopener">https://segmentfault.com/a/1190000014178462?utm_source=tag-newest</a></p>
<p>[6] <a href="https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E8%AA%9E%E6%B3%95%E6%A8%B9" target="_blank" rel="noopener">https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E8%AA%9E%E6%B3%95%E6%A8%B9</a></p>
<p>[7] <a href="https://itnext.io/ast-for-javascript-developers-3e79aeb08343" target="_blank" rel="noopener">https://itnext.io/ast-for-javascript-developers-3e79aeb08343</a></p>
<p>[8] <a href="https://babeljs.io/docs/en/next/babel-types.html#callexpression" target="_blank" rel="noopener">https://babeljs.io/docs/en/next/babel-types.html#callexpression</a></p>
<p>[9] <a href="https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart" target="_blank" rel="noopener">https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart</a></p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/05/29/ast-有魅力的绿巨人-第二回合-丁岳/" data-id="cjw8n3wjz0002ucjtwnc0r5tj" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-20181204-使用AST将VUE组件转换为微信小程序组件-丁岳" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/05/29/20181204-使用AST将VUE组件转换为微信小程序组件-丁岳/" class="article-date">
<time datetime="2019-05-29T02:55:14.314Z" itemprop="datePublished">2019-05-29</time>
</a>
</div>
<div class="article-inner">
<div class="article-entry" itemprop="articleBody">
<h2 id="通过操作抽象语法树将-VUE-组件转换为微信小程序组件"><a href="#通过操作抽象语法树将-VUE-组件转换为微信小程序组件" class="headerlink" title="通过操作抽象语法树将 VUE 组件转换为微信小程序组件"></a>通过操作抽象语法树将 VUE 组件转换为微信小程序组件</h2><p>简介:<br>首先我们介绍一下本文的关键点:抽象语法树,它是以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。<br>通过操作这棵树,可以精确的定位到声明、赋值、运算语句,从而实现对代码的优化、变更等操作。<br>本文通过对 VUE 组件的 JavaScript 、CSS模块进行转换,比如 JavaScript模块,包括外层对象,生命周期钩子函数,赋值语句,组件的内部数据,组件的对外属性等等来实现把一个 VUE 组件转换为 一个小程序组件。</p>
<p>AST 抽象语法树,似乎我们平时并不会接触到。实际上在我们的项目当中,CSS 预处理,JSX 亦或是 TypeScript 的处理,代码格式化美化工具,Eslint, Javascript 转译,代码压缩,Webpack, Vue-Cli,ES6 转 ES5,当中都离不开 AST 抽象语法树这个绿巨人。先卖个关子,让我们看一下 AST 到的官方解释:</p>
<blockquote>
<p>It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.</p>
</blockquote>
<p>中文的解释有:<br>抽象语法树(abstract syntax tree或者缩写为 AST ),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。和抽象语法树相对的是具体语法树(concrete syntaxtree),通常称作分析树(parse tree)。一般的,在源代码的翻译和编译过程中,语法分析器创建出分析树。一旦 AST 被创建出来,在后续的处理过程中,比如语义分析阶段,会添加一些信息。<br>抽象语法树,它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。<br>通过操作这棵树,可以精确的定位到声明、赋值、运算语句,从而实现对代码的优化、变更等操作。这些并不是我们想要看到的。</p>
<p><img src="http://img11.360buyimg.com/uba/jfs/t29965/285/722902525/72447/89fe9e32/5bfca98cN2a4fc2dc.png"></p>
<p>对于 AST 面纱的神秘感,似乎已经将要揭开。不错,在我刚接触到他的时候,同样感觉确实是难。但是当你开始了解了它以后,你就会越来越喜欢它。<br>因为他实在太强大了。AST 本身并不难,难点在于转换的目标对象与源对象的语法差异,当中水深毋庸置疑。但是,这才能更加激起我们探索他的欲望。在开始之前,我们先看一下 抽象语法树到底长什么样子。</p>
<h2 id="一、-一探究竟-AST"><a href="#一、-一探究竟-AST" class="headerlink" title="一、 一探究竟 AST"></a>一、 一探究竟 AST</h2><p>通过 <a href="https://astexplorer.net/" target="_blank" rel="noopener">astexplorer</a> [1] (AST树查看网站),通过他你可以方便的查看代码的语法树,挑你喜欢的库。你可以在线把玩 AST,而且除了 JavaScript,HTML, CSS 还有很多其它语言的 AST 库,让我们对他有一个感性而直观的认识。<br>请看下图,看看AST语法树长什么样子:</p>
<p><img src="http://img14.360buyimg.com/uba/jfs/t1/8079/13/16060/9637/5c75193fEbfbc926d/b0eb0894b0370be5.png"><br>此图看到的是一个 ExportDefaultDeclaration 也就是export default {},还有他的位置信息,注释失信,tokens等等。</p>
<h6 id="国际惯例,先来一个小demo"><a href="#国际惯例,先来一个小demo" class="headerlink" title="国际惯例,先来一个小demo"></a>国际惯例,先来一个小demo</h6><h6 id="输入数据"><a href="#输入数据" class="headerlink" title="输入数据"></a>输入数据</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">square</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> n * n;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h6 id="处理数据"><a href="#处理数据" class="headerlink" title="处理数据"></a>处理数据</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">astFn() {</span><br><span class="line"> <span class="keyword">const</span> code = <span class="string">`function square(n) {</span></span><br><span class="line"><span class="string"> return n * n;</span></span><br><span class="line"><span class="string"> }`</span>;</span><br><span class="line"> <span class="comment">//得到 AST 语法树</span></span><br><span class="line"> <span class="keyword">const</span> ast = babylon.parse(code);</span><br><span class="line"></span><br><span class="line"> traverse(ast, {</span><br><span class="line"> enter(path) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"enter: "</span> + path.node.type);</span><br><span class="line"> <span class="comment">//如下的语句匹配到 return 中的 n 与参数 n,并且将它替换为 x。</span></span><br><span class="line"> <span class="keyword">if</span> (path.node.type === <span class="string">"Identifier"</span> && path.node.name === <span class="string">"n"</span>) {</span><br><span class="line"> path.node.name = <span class="string">"x"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> generate(ast, {}, code);<span class="comment">//将 AST 转换为代码</span></span><br><span class="line"> <span class="built_in">console</span>.log(generate(ast, {}, code).code );<span class="comment">//打印出转换后的 JavaScript 代码</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 js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">square</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x * x;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>我们看一下我们得到的 AST 树<br><br><img src="http://img14.360buyimg.com/uba/jfs/t26656/262/2307176011/9469/12844ea3/5bfcc13aN0d2bb9a2.png"><br><br>接下来我们插入一段 把 VUE 组件转换为微信小程序组件正则版本的处理</p>
<h2 id="二、-简单粗暴的版本(VUE-组件转换为微信小程序组件)"><a href="#二、-简单粗暴的版本(VUE-组件转换为微信小程序组件)" class="headerlink" title="二、 简单粗暴的版本(VUE 组件转换为微信小程序组件)"></a>二、 简单粗暴的版本(VUE 组件转换为微信小程序组件)</h2><p>没有使用 AST 将 VUE 组件转换成小程序组件的简易版本介绍<br>下方是两段代码,简单的逻辑,实现思路,匹配目标字符串,替换字符,然后生成文件。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">regs:{<span class="comment">//通过标签匹配来替换对应的小程序支持的标签</span></span><br><span class="line"> toViewStartTags: <span class="regexp">/(<h1)|(<s)|(<em)|(<ul)|(<li)|(<dl)|(<i)|(<span)/g</span>,</span><br><span class="line"> toViewEndTags: <span class="regexp">/(<\/h1>)|(<\/s>)|(<\/em>)|(<\/ul>)|(<\/li>)|(<\/dl>)|(<\/i>)|(<\/span>)/g</span>,</span><br><span class="line"> toBlockStartTags: <span class="regexp">/(<div)|(<p)/g</span>,</span><br><span class="line"> toBlockEndTags: <span class="regexp">/(<\/div>)|(<\/p>)/g</span>,</span><br><span class="line">},</span><br><span class="line">signObj: {<span class="comment">//通过标签查找来分离脚本,模板和CSS</span></span><br><span class="line"> tempStart: <span class="string">'<template>'</span>,</span><br><span class="line"> tempEnd: <span class="string">'</template>'</span>,</span><br><span class="line"> styleStart: <span class="string">'<style scoped>'</span>,</span><br><span class="line"> styleEnd: <span class="string">'</style>'</span>,</span><br><span class="line"> scriptStart: <span class="string">'<script>'</span>,</span><br><span class="line"> scriptEnd: <span class="string">'</script>'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>上方是正则版本的一些模板匹配规则,经过后续的一系列处理把一个 VUE组件处理得到对应的小程序的 WXML ,WXSS,JSON,JS,4个文件。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//文件</span></span><br><span class="line"><span class="keyword">const</span> wxssFilePath = path.join(dirPath, <span class="string">`<span class="subst">${mpFile}</span>.wxss`</span>);</span><br><span class="line"><span class="keyword">const</span> jsFilePath = path.join(dirPath, <span class="string">`<span class="subst">${mpFile}</span>.js`</span>);</span><br><span class="line"><span class="keyword">const</span> wxmlFilePath = path.join(dirPath, <span class="string">`<span class="subst">${mpFile}</span>.wxml`</span>);</span><br><span class="line"><span class="keyword">const</span> jsonFilePath = path.join(dirPath, <span class="string">`<span class="subst">${mpFile}</span>.json`</span>);</span><br><span class="line"><span class="keyword">if</span> (!fs.existsSync(dirPath)) {</span><br><span class="line"> fs.mkdirSync(dirPath);</span><br><span class="line">}</span><br><span class="line">fs.writeFile(wxssFilePath, wxssContent, err => {</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="comment">//console.log(`dist目录下生成${mpFile}.wxss文件成功`);</span></span><br><span class="line"> fs.writeFile(jsFilePath, jsContent, err => {</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="comment">// console.log(`dist目录下生成${mpFile}.js文件成功`);</span></span><br><span class="line"> fs.writeFile(wxmlFilePath, wxmlContent, err => {</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="comment">//console.log(`dist目录下生成${mpFile}.wxml文件成功`);</span></span><br><span class="line"> fs.writeFile(jsonFilePath, jsonContent, err => {</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`dist目录下生成<span class="subst">${mpFile}</span>.json文件成功`</span>)</span><br><span class="line"> resolve(<span class="string">`生成<span class="subst">${mpFile}</span>.json文件成功`</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>上方是处理得到的 WXML ,WXSS,JSON,JS,4个文件,并且生成到对应的目录下。代码实现用时较短,后续更改方案,并没有做优化,这里就不做详细展开讨论这个实现方案了。</p>
<p>回到正题 介绍一下,AST 抽象语法树的核心部分:</p>
<h2 id="三、-抽象语法树三大法宝"><a href="#三、-抽象语法树三大法宝" class="headerlink" title="三、 抽象语法树三大法宝"></a>三、 抽象语法树三大法宝</h2><p>Babel 是 JavaScript 编译器,更确切地说是源码到源码的编译器,通常也叫做“ 转换编译器(transpiler)”。 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。</p>
<p>babel-core:Babel 的编译器;它暴露了 babel.transform 方法。</p>
<p>[1] babylon:Babylon 是 Babel 的解析器。用于生成 AST 语法树。</p>
<p>[2] babel-traverse:Babel 的遍历器,所有的transformers都使用该工具遍历所有的 AST (抽象语法树),维护了整棵树的状态,并且负责替换、移除和添加节点。我们可以和 Babylon 一起使用来遍历和更新节点。</p>
<p>[3] babel-generator:Babel 的代码生成器。它读取AST并将其转换为代码。</p>
<p>整个编译器就被分成了三部分:分析器、转换器、生成器,大致的流程是:</p>
<p>输入字符串 -> babylon分析器 parse -> 得到 AST -> 转换器 -> 得到 AST -> babel-generator -> 输出</p>
<h5 id="总结核心三步:"><a href="#总结核心三步:" class="headerlink" title="总结核心三步:"></a>总结核心三步:</h5><p>AST 三大法宝<br>babylon.parse => traverse 转换 AST => generate(ast, {}, code).code) 生成</p>
<p>感兴趣的童鞋,可以在网上或者看参考资料都有介绍。该铺垫的都铺垫的差不多了,进入正题。</p>
<h3 id="我们到底是如何通过-AST-将-VUE-组件转换为微信小程序组件的呢?"><a href="#我们到底是如何通过-AST-将-VUE-组件转换为微信小程序组件的呢?" class="headerlink" title="我们到底是如何通过 AST 将 VUE 组件转换为微信小程序组件的呢?"></a>我们到底是如何通过 AST 将 VUE 组件转换为微信小程序组件的呢?</h3><h2 id="四、-VUE-组件转换为微信小程序组件中-组件的对外属性、赋值语句的转换处理"><a href="#四、-VUE-组件转换为微信小程序组件中-组件的对外属性、赋值语句的转换处理" class="headerlink" title="四、 VUE 组件转换为微信小程序组件中 组件的对外属性、赋值语句的转换处理"></a>四、 VUE 组件转换为微信小程序组件中 组件的对外属性、赋值语句的转换处理</h2><h6 id="转换之前的-VUE-组件代码-Demo"><a href="#转换之前的-VUE-组件代码-Demo" class="headerlink" title="转换之前的 VUE 组件代码 Demo"></a>转换之前的 VUE 组件代码 Demo</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> <span class="comment">//组件的对外属性</span></span><br><span class="line"> props: {</span><br><span class="line"> max: {</span><br><span class="line"> type: <span class="built_in">Number</span>,</span><br><span class="line"> value: <span class="number">99</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"> data(){</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> num:<span class="number">10000</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"> methods: {</span><br><span class="line"> textFn() {</span><br><span class="line"> <span class="keyword">this</span>.num = <span class="number">2</span></span><br><span class="line"> },</span><br><span class="line"> onMyButtonTap: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.num = <span class="number">666</span></span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h6 id="处理后我们得到的微信小程序组件-JavaScript-部分代码"><a href="#处理后我们得到的微信小程序组件-JavaScript-部分代码" class="headerlink" title="处理后我们得到的微信小程序组件 JavaScript 部分代码"></a>处理后我们得到的微信小程序组件 JavaScript 部分代码</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> properties: {</span><br><span class="line"> <span class="comment">//组件的对外属性</span></span><br><span class="line"> max: {</span><br><span class="line"> type: <span class="built_in">Number</span>,</span><br><span class="line"> value: <span class="number">99</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"> data(){</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> num: <span class="number">10000</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"> methods: {</span><br><span class="line"> textFn() {</span><br><span class="line"> <span class="keyword">this</span>.setData({</span><br><span class="line"> num: <span class="number">2</span></span><br><span class="line"> });</span><br><span class="line"> },</span><br><span class="line"> onMyButtonTap: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.setData({</span><br><span class="line"> num: <span class="number">666</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>
<h6 id="我们对js动了什么手脚-亦可封装成babel插件"><a href="#我们对js动了什么手脚-亦可封装成babel插件" class="headerlink" title="我们对js动了什么手脚(亦可封装成babel插件):"></a>我们对js动了什么手脚(亦可封装成babel插件):</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//to AST</span></span><br><span class="line"><span class="keyword">const</span> ast = babylon.parse(code, {</span><br><span class="line"> sourceType: <span class="string">"module"</span>,</span><br><span class="line"> plugins: [<span class="string">"flow"</span>]</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//AST 转换 node,nodetype很关键</span></span><br><span class="line">traverse(ast, {</span><br><span class="line"> enter(path) {</span><br><span class="line"> <span class="comment">//打印出node.type</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"enter: "</span> + path.node.type);</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">ObjectProperty(path) {</span><br><span class="line"> <span class="comment">//props 替换为 properties</span></span><br><span class="line"> <span class="keyword">if</span> (path.node.key.name === <span class="string">"props"</span>) {</span><br><span class="line"> path.node.key.name = <span class="string">"properties"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//修改methods中使用到数据属性的方法中。this.prop至this.data.prop等 与 this.setData的处理。</span></span><br><span class="line">MemberExpression(path) {</span><br><span class="line"> <span class="keyword">let</span> datasVals = datas.map(<span class="function">(<span class="params">item,index</span>) =></span>{</span><br><span class="line"> <span class="keyword">return</span> item.key.name <span class="comment">//拿到data属性中的第一级</span></span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">if</span> (<span class="comment">//含有this的表达式 并且包含data属性</span></span><br><span class="line"> path.node.object.type === <span class="string">"ThisExpression"</span> &&</span><br><span class="line"> datasVals.includes(path.node.property.name)</span><br><span class="line"> ) {</span><br><span class="line"> path.get(<span class="string">"object"</span>).replaceWithSourceString(<span class="string">"this.data"</span>);</span><br><span class="line"> <span class="comment">//判断是不是赋值操作</span></span><br><span class="line"> <span class="keyword">if</span> (</span><br><span class="line"> (t.isAssignmentExpression(path.parentPath) &&</span><br><span class="line"> path.parentPath.get(<span class="string">"left"</span>) === path) ||</span><br><span class="line"> t.isUpdateExpression(path.parentPath)</span><br><span class="line"> ) {</span><br><span class="line"> <span class="keyword">const</span> expressionStatement = path.findParent(<span class="function"><span class="params">parent</span> =></span></span><br><span class="line"> parent.isExpressionStatement()</span><br><span class="line"> );</span><br><span class="line"> <span class="comment">//......</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h6 id="转换之前的js代码的部分-AST-树:"><a href="#转换之前的js代码的部分-AST-树:" class="headerlink" title="转换之前的js代码的部分 AST 树:"></a>转换之前的js代码的部分 AST 树:</h6><p><img src="http://img12.360buyimg.com/uba/jfs/t30847/232/734714251/110991/96f7e8c6/5bfcb972N9dd3d8cb.png"><br></p>
<h6 id="具体的-API-使用,童鞋们看一下-babel-相关的文档了解一下。"><a href="#具体的-API-使用,童鞋们看一下-babel-相关的文档了解一下。" class="headerlink" title="具体的 API 使用,童鞋们看一下 babel 相关的文档了解一下。"></a>具体的 API 使用,童鞋们看一下 babel 相关的文档了解一下。</h6><h2 id="五,-VUE-组件转换为微信小程序组件中-Export-Default-到-Component-构造器的转换-与-生命周期钩子函数,事件函数的处理"><a href="#五,-VUE-组件转换为微信小程序组件中-Export-Default-到-Component-构造器的转换-与-生命周期钩子函数,事件函数的处理" class="headerlink" title="五, VUE 组件转换为微信小程序组件中 Export Default 到 Component 构造器的转换 与 生命周期钩子函数,事件函数的处理"></a>五, VUE 组件转换为微信小程序组件中 Export Default 到 Component 构造器的转换 与 生命周期钩子函数,事件函数的处理</h2><p>首先我们看一下要转换前后的语法树与代码如下(明确转换目标):</p>
<h6 id="转换之前的-AST-树与代码"><a href="#转换之前的-AST-树与代码" class="headerlink" title="转换之前的 AST 树与代码"></a>转换之前的 AST 树与代码</h6><p><img src="http://img11.360buyimg.com/uba/jfs/t29581/123/1031219550/7088/28f16ea1/5c05dddfN076efa20.png"></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {<span class="comment">// VUE 组件的惯用写法用于导出对象模块</span></span><br><span class="line"> data(){</span><br><span class="line"> },</span><br><span class="line"> methods:{</span><br><span class="line"> },</span><br><span class="line"> props:{</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h6 id="转换之后的-AST-树与代码"><a href="#转换之后的-AST-树与代码" class="headerlink" title="转换之后的 AST 树与代码"></a>转换之后的 AST 树与代码</h6><p><img src="http://img11.360buyimg.com/uba/jfs/t26155/227/2594893184/9762/8d47d607/5c05dddfNe823c12a.png"></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">components({<span class="comment">//小程序组件的构造器</span></span><br><span class="line"> data(){</span><br><span class="line"> },</span><br><span class="line"> methods:{</span><br><span class="line"> },</span><br><span class="line"> props:{</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>通过以上转换之前和转换之后代码和 AST 的对比我们明确了转换目标就是 ExportDefault 到 Component构造器的转换,下面看一下我们是如何处理的:</p>
<h6 id="我们做了什么(在转换中进入到-ExportDefault-中做对应的处理)"><a href="#我们做了什么(在转换中进入到-ExportDefault-中做对应的处理)" class="headerlink" title="我们做了什么(在转换中进入到 ExportDefault 中做对应的处理):"></a>我们做了什么(在转换中进入到 ExportDefault 中做对应的处理):</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ExportDefault 到 Component构造器的转换</span></span><br><span class="line">ExportDefaultDeclaration(path) {</span><br><span class="line"><span class="comment">//创建 CallExpression Component({})</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">insertBeforeFn</span>(<span class="params">path</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> objectExpression = t.objectExpression(propertiesAST);</span><br><span class="line"> test = t.expressionStatement(</span><br><span class="line"> t.callExpression(<span class="comment">//创建名为 Compontents 的调用表达式,参数为 objectExpression</span></span><br><span class="line"> t.identifier(<span class="string">"Compontents"</span>),[</span><br><span class="line"> objectExpression</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="built_in">console</span>.log(<span class="string">"test"</span>,test)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (path.node.type === <span class="string">"ExportDefaultDeclaration"</span>) {</span><br><span class="line"> <span class="keyword">if</span> (path.node.declaration.properties) {</span><br><span class="line"> <span class="comment">//提取属性并存储</span></span><br><span class="line"> propertiesAST = path.node.declaration.properties;</span><br><span class="line"> <span class="comment">//创建 AST 包裹对象</span></span><br><span class="line"> insertBeforeFn(path); </span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//得到我们最终的转换结果</span></span><br><span class="line"> <span class="built_in">console</span>.log(generate(test, {}, code).code);</span><br></pre></td></tr></table></figure>
<p>对于 ExportDefault => Component 构造器转换还有一种转换思路 下面我们看一下:<br><br>[1] 第一种思路是先提取 ExportDefault 内部所有节点的 AST ,并做处理,然后创建Component构造器,插入提取处理后的 AST,得到最终的 AST</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//propertiesAST 这个就是我们拿到的 AST,然后在对应的分支内做对应的处理 以下分别为 data,methods,props,其他的钩子同样处理即可</span></span><br><span class="line">propertiesAST.map(<span class="function">(<span class="params">item, index</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (item.type === <span class="string">"ObjectProperty"</span>) {</span><br><span class="line"> <span class="comment">//props 替换为 properties</span></span><br><span class="line"> <span class="keyword">if</span> (item.key.name === <span class="string">"props"</span>) {</span><br><span class="line"> item.key.name = <span class="string">"properties"</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (item.type === <span class="string">"ObjectMethod"</span>) {</span><br><span class="line"> <span class="keyword">if</span> (path.node.key.name === <span class="string">"mounted"</span>) {</span><br><span class="line"> path.node.key.name = <span class="string">"ready"</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (path.node.key.name === <span class="string">"created"</span>) {</span><br><span class="line"> path.node.key.name = <span class="string">"attached"</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (path.node.key.name === <span class="string">"destroyed"</span>) {</span><br><span class="line"> path.node.key.name = <span class="string">"detached"</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (path.node.type === <span class="string">"ThisExpression"</span>) {</span><br><span class="line"> <span class="keyword">if</span> (path.parent.property.name === <span class="string">"$emit"</span>) {</span><br><span class="line"> path.parent.property.name = <span class="string">"triggerEvent"</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">void</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (path.node.key.name === <span class="string">"methods"</span>) {</span><br><span class="line"> path.traverse({</span><br><span class="line"> enter(path) {</span><br><span class="line"> <span class="keyword">if</span> (path.node.type === <span class="string">"ThisExpression"</span>) {</span><br><span class="line"> <span class="keyword">if</span> (path.parent.property.name === <span class="string">"$emit"</span>) {</span><br><span class="line"> path.parent.property.name = <span class="string">"triggerEvent"</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><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"node type"</span>, item.type);</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>[2] 第二种思路呢,就是我们上面展示的这种,不过有一个关键的地方要注意一下:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//我把 ExportDefaultDeclaration 的处理放到最后来执行,拿到 AST 首先进行转换。然后在创建得到新的小程序组件JS部分的 AST 即可</span></span><br><span class="line">traverse(ast, {</span><br><span class="line"> enter(path) {},</span><br><span class="line"> ObjectProperty(path) {},</span><br><span class="line"> ObjectMethod(path) {},</span><br><span class="line"> <span class="comment">//......</span></span><br><span class="line"> ExportDefaultDeclaration(path) {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>如果你想在 AST 开始处与结尾处插入,可使用 path 操作:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">path.insertBefore(</span><br><span class="line"> t.expressionStatement(t.stringLiteral(<span class="string">"start.."</span>))</span><br><span class="line">);</span><br><span class="line">path.insertAfter(</span><br><span class="line"> t.expressionStatement(t.stringLiteral(<span class="string">"end.."</span>))</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<p>注:关于微信小程序不支持 computed , 与 watch,我们具体的初期采用的方案是挂载 computed 和 watch 方法到每一个微信小程序组件,让小程序组件也支持这两个功能。</p>
<h2 id="六,VUE-组件转换为微信小程序组件中-的-Data-部分的处理:"><a href="#六,VUE-组件转换为微信小程序组件中-的-Data-部分的处理:" class="headerlink" title="六,VUE 组件转换为微信小程序组件中 的 Data 部分的处理:"></a>六,VUE 组件转换为微信小程序组件中 的 Data 部分的处理:</h2><p>关于 Data 部分的处理实际上就是:函数表达式转换为对象表达式 (FunctionExpression 转换为 ObjectExpression)</p>
<h6 id="转换之前的-JavaScript-代码"><a href="#转换之前的-JavaScript-代码" class="headerlink" title="转换之前的 JavaScript 代码"></a>转换之前的 JavaScript 代码</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> data(){<span class="comment">//函数表达式</span></span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> num: <span class="number">10000</span>,</span><br><span class="line"> arr: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>],</span><br><span class="line"> obj: {</span><br><span class="line"> d1: <span class="string">"val1"</span>,</span><br><span class="line"> d2: <span class="string">"val2"</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>
<h6 id="处理后我们得到的"><a href="#处理后我们得到的" class="headerlink" title="处理后我们得到的"></a>处理后我们得到的</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> data: {<span class="comment">//对象表达式</span></span><br><span class="line"> num: <span class="number">10000</span>,</span><br><span class="line"> arr: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>],</span><br><span class="line"> obj: {</span><br><span class="line"> d1: <span class="string">"val1"</span>,</span><br><span class="line"> d2: <span class="string">"val2"</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>
<h6 id="转换前后-AST-树对比图明确转换目标:"><a href="#转换前后-AST-树对比图明确转换目标:" class="headerlink" title="转换前后 AST 树对比图明确转换目标:"></a>转换前后 AST 树对比图明确转换目标:</h6><p><img src="http://img11.360buyimg.com/uba/jfs/t28720/269/1084213324/12861/fb20d3a5/5c05e360N576b3290.png"><br><br><img src="http://img30.360buyimg.com/uba/jfs/t29215/312/1056174776/12311/67b44b9f/5c05e384Nb98eff0b.png"><br></p>
<h6 id="我们对-JavaScript-动了什么手脚-亦可封装成babel插件"><a href="#我们对-JavaScript-动了什么手脚-亦可封装成babel插件" class="headerlink" title="我们对 JavaScript 动了什么手脚(亦可封装成babel插件):"></a>我们对 JavaScript 动了什么手脚(亦可封装成babel插件):</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ast = babylon.parse(code, {</span><br><span class="line"> sourceType: <span class="string">"module"</span>,</span><br><span class="line"> plugins: [<span class="string">"flow"</span>]</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//AST 转换node、nodetype很关键</span></span><br><span class="line">traverse(ast, {</span><br><span class="line"> enter(path) {</span><br><span class="line"> <span class="comment">//打印出node.type</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"enter: "</span> + path.node.type);</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<h6 id="我们的转换部分都尽量在一个-Traverse-中处理,减少-AST-树遍历的性能消耗"><a href="#我们的转换部分都尽量在一个-Traverse-中处理,减少-AST-树遍历的性能消耗" class="headerlink" title="我们的转换部分都尽量在一个 Traverse 中处理,减少 AST 树遍历的性能消耗"></a>我们的转换部分都尽量在一个 Traverse 中处理,减少 AST 树遍历的性能消耗</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Data 函数表达式 转换为 Object</span></span><br><span class="line">ObjectMethod(path) {</span><br><span class="line"> <span class="comment">// console.log("path.node ",path.node )// data, add, textFn</span></span><br><span class="line"> <span class="keyword">if</span> (path.node.key.name === <span class="string">"data"</span>) {</span><br><span class="line"> <span class="comment">// 获取第一级的 BlockStatement,也就是 Data 函数体</span></span><br><span class="line"> path.traverse({</span><br><span class="line"> BlockStatement(p) {</span><br><span class="line"> <span class="comment">//从 Data 中提取数据属性</span></span><br><span class="line"> datas = p.node.body[<span class="number">0</span>].argument.properties;</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">const</span> objectExpression = t.objectExpression(datas);</span><br><span class="line"> <span class="comment">//创建 Data 对象并赋值</span></span><br><span class="line"> <span class="keyword">const</span> dataProperty = t.objectProperty(</span><br><span class="line"> t.identifier(<span class="string">"data"</span>),</span><br><span class="line"> objectExpression</span><br><span class="line"> );</span><br><span class="line"> <span class="comment">//插入到原 Data 函数下方</span></span><br><span class="line"> path.insertAfter(dataProperty);</span><br><span class="line"> <span class="comment">//删除原 Data 函数节点</span></span><br><span class="line"> path.remove();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="七,VUE-组件转换为微信小程序组件中-CSS-部分的处理:"><a href="#七,VUE-组件转换为微信小程序组件中-CSS-部分的处理:" class="headerlink" title="七,VUE 组件转换为微信小程序组件中 CSS 部分的处理:"></a>七,VUE 组件转换为微信小程序组件中 CSS 部分的处理:</h2><p>那 CSS 我们也是必须要处理的一部分,let try</p>
<h6 id="以下是我们要处理的css样本"><a href="#以下是我们要处理的css样本" class="headerlink" title="以下是我们要处理的css样本"></a>以下是我们要处理的css样本</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">const code = `</span><br><span class="line"> <span class="selector-class">.text-ok</span>{</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#e4393c</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.nut-popup-close</span>{</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">50px</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">120px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">26px</span>;</span><br><span class="line"> }`;</span><br></pre></td></tr></table></figure>
<h6 id="处理后我们得到的-1"><a href="#处理后我们得到的-1" class="headerlink" title="处理后我们得到的"></a>处理后我们得到的</h6><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.text-ok</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">351</span>rpx;</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#e4393c</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.nut-popup-close</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">117</span>rpx;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">280.79</span>rpx;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">468</span>rpx;</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">60.84</span>rpx;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>通过前后代码的对比,我们看到了单位尺寸的转换(比如:top: 50px; 转换为 top: 117rpx;)。<br>单位的转换( px 转为了 rpx )</p>
<h6 id="CSS-又做了哪些处理呢?"><a href="#CSS-又做了哪些处理呢?" class="headerlink" title="CSS 又做了哪些处理呢?"></a>CSS 又做了哪些处理呢?</h6><p>同样也有不少的 CSS Code Parsers 供我们选择 Cssom ,CssTree等等,<br>我们拿 Cssom 来实现上方css代码的一个简单的转换。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ast = csstree.parse(code);</span><br><span class="line"> csstree.walk(ast, <span class="function"><span class="keyword">function</span>(<span class="params">node</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">typeof</span> node.value == <span class="string">"string"</span> && <span class="built_in">isNaN</span>(node.value) != <span class="literal">true</span>){</span><br><span class="line"> <span class="keyword">let</span> newVal = <span class="built_in">Math</span>.floor((node.value*<span class="number">2.34</span>) * <span class="number">100</span>) / <span class="number">100</span>;<span class="comment">//转换比例这个根据情况设置即可</span></span><br><span class="line"> <span class="keyword">if</span>(node.type === <span class="string">"Dimension"</span>){<span class="comment">//得到要转换的数字尺寸</span></span><br><span class="line"> node.value = newVal;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(node.unit === <span class="string">"px"</span>){<span class="comment">//单位的处理</span></span><br><span class="line"> node.unit = <span class="string">"rpx"</span></span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="built_in">console</span>.log(csstree.generate(ast));</span><br></pre></td></tr></table></figure>
<p>当然这只是一个 demo,实际项目中使用还的根据项目的实际情况出发,SCSS,LESS等等的转换与考虑不同的处理场景哦!</p>
<p>注:本文有些模块的转换实现还未在小程序开发工具中测试。</p>
<p>插播一个通过 AST 实现的好东东:</p>
<h6 id="将-JavaScript-代码转化生成-SVG-流程图-js2flowchart-4-5-k-stars-在-GitHub"><a href="#将-JavaScript-代码转化生成-SVG-流程图-js2flowchart-4-5-k-stars-在-GitHub" class="headerlink" title="将 JavaScript 代码转化生成 SVG 流程图 js2flowchart( 4.5 k stars 在 GitHub )"></a>将 JavaScript 代码转化生成 SVG 流程图 js2flowchart( 4.5 k stars 在 GitHub )</h6><p>当你拥有 AST 时,可以做任何你想要做的事。把AST转回成字符串代码并不是必要的,你可以通过它画一个流程图,或者其它你想要的东西。<br>js2flowchart使用场景是什么呢?通过流程图,你可以解释你的代码,或者给你代码写文档;通过可视化的解释学习其他人的代码;通过简单的js语法,为每个处理过程简单的描述创建流程图。<br>马上用最简单的方式尝试一下吧,去线上编辑看看 <a href="https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart/" target="_blank" rel="noopener">js-code-to-svg-flowchart</a> [8]。<br>此处有必要附上截图一张。</p>
<p><img src="http://img10.360buyimg.com/uba/jfs/t28051/66/739453023/25363/43b62be0/5bfd0d0aNb5f485c5.png"></p>
<h2 id="八、总结:"><a href="#八、总结:" class="headerlink" title="八、总结:"></a>八、总结:</h2><p>通过以上我们的介绍,我们大概对抽象语法树有了初步的了解。总体思路是:我们用Babel的解析器 把 JavaScript 源码转化为抽象语法树,<br>再通过 Babel 的遍历器遍历 AST (抽象语法树),替换、移除和添加节点,得到一个新的 AST 树。最后, 使用,Babel 的代码生成器 Babel Generator 模块 读取 处理后的 AST 并将其转换为代码。任务就完成了!</p>
<p>本文通过对 VUE 组件转换为微信小程序组件的转换部分包括如下内容:</p>
<ol>
<li>VUE 组件 JavaScript模块 对外属性转换为小程序对外属性的处理</li>
<li>VUE 组件 JavaScript模块 内部数据的转换为小程序内部数据的处理</li>
<li>VUE 组件 JavaScript模块 methods 中的赋值语句转换为小程序赋值语句的处理</li>
<li>VUE 组件 JavaScript模块 外层对象,生命周期钩子函数的处理与 CSS 模块的简易处理</li>
</ol>
<p>希望,本文对大家有所帮助,在技术探索的路上,我们一往无前, Paladin 精神永存!<br>感谢大家的耐心阅读,也欢迎大家关注【全栈探索公众号】,每周都会有技术好文推出!</p>
<h2 id="扩展阅读"><a href="#扩展阅读" class="headerlink" title="扩展阅读"></a>扩展阅读</h2><p>[1] <a href="https://astexplorer.net/" target="_blank" rel="noopener">https://astexplorer.net/</a></p>
<p>[2] <a href="https://babeljs.io/docs/en/next/babel-types.html#objectexpression" target="_blank" rel="noopener">https://babeljs.io/docs/en/next/babel-types.html#objectexpression</a> </p>
<p>[3] <a href="https://github.com/babel/babel/tree/6.x/packages/babel-types" target="_blank" rel="noopener">https://github.com/babel/babel/tree/6.x/packages/babel-types</a></p>
<p>[4] <a href="http://esprima.org/demo/parse.html#" target="_blank" rel="noopener">http://esprima.org/demo/parse.html#</a></p>
<p>[5] <a href="https://segmentfault.com/a/1190000014178462?utm_source=tag-newest" target="_blank" rel="noopener">https://segmentfault.com/a/1190000014178462?utm_source=tag-newest</a></p>
<p>[6] <a href="https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E8%AA%9E%E6%B3%95%E6%A8%B9" target="_blank" rel="noopener">https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E8%AA%9E%E6%B3%95%E6%A8%B9</a></p>
<p>[7] <a href="https://itnext.io/ast-for-javascript-developers-3e79aeb08343" target="_blank" rel="noopener">https://itnext.io/ast-for-javascript-developers-3e79aeb08343</a></p>
<p>[8] <a href="https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart" target="_blank" rel="noopener">https://github.com/Bogdan-Lyashenko/js-code-to-svg-flowchart</a></p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/05/29/20181204-使用AST将VUE组件转换为微信小程序组件-丁岳/" data-id="cjw8n3wjn0001ucjtk1v81hsp" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-20181204-ast-有魅力的绿巨人-第一回合-丁岳" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/05/29/20181204-ast-有魅力的绿巨人-第一回合-丁岳/" class="article-date">
<time datetime="2019-05-29T02:55:14.286Z" itemprop="datePublished">2019-05-29</time>
</a>
</div>
<div class="article-inner">
<div class="article-entry" itemprop="articleBody">
<h2 id="AST-抽象语法树无处不在-第一回合"><a href="#AST-抽象语法树无处不在-第一回合" class="headerlink" title="AST 抽象语法树无处不在-第一回合"></a>AST 抽象语法树无处不在-第一回合</h2><p>简介:<br>抽象语法树,它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。<br>通过操作这棵树,可以精确的定位到声明、赋值、运算语句,从而实现对代码的优化、变更等操作。</p>
<p>AST 抽象语法树,似乎我们平时并不会接触到。实际上在我们的项目当中,CSS 预处理,JSX 亦或是 TypeScript 的处理,代码格式化美化工具,Eslint, Javascript 转译,代码压缩,Webpack, Vue-Cli,ES6 转 ES5,当中都离不开 AST 抽象语法树这个绿巨人。先卖个关子,让我们看一下 AST 到的官方解释:</p>
<blockquote>
<p>It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.</p>
</blockquote>
<p>中文的解释有:<br>抽象语法树(abstract syntax tree或者缩写为 AST ),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。和抽象语法树相对的是具体语法树(concrete syntaxtree),通常称作分析树(parse tree)。一般的,在源代码的翻译和编译过程中,语法分析器创建出分析树。一旦 AST 被创建出来,在后续的处理过程中,比如语义分析阶段,会添加一些信息。<br>抽象语法树,它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。<br>通过操作这棵树,可以精确的定位到声明、赋值、运算语句,从而实现对代码的优化、变更等操作。这些并不是我们想要看到的。</p>
<p><img src="http://img11.360buyimg.com/uba/jfs/t29965/285/722902525/72447/89fe9e32/5bfca98cN2a4fc2dc.png"></p>
<p>对于 AST 面纱的神秘感,似乎已经将要揭开。不错,在我刚接触到他的时候,同样感觉确实是难。但是当你开始了解了它以后,你就会越来越喜欢它。<br>因为他实在太强大了。AST 的水深是毋庸置疑的。但是,这才能更加激起我们探索他的欲望。</p>
<h2 id="一、-一探究竟-AST"><a href="#一、-一探究竟-AST" class="headerlink" title="一、 一探究竟 AST"></a>一、 一探究竟 AST</h2><p>通过 <a href="https://astexplorer.net/" target="_blank" rel="noopener">astexplorer</a> [1] (AST树查看网站),通过他你可以方便的查看代码的语法树,挑你喜欢的库。你可以在线把玩 AST,而且除了 JavaScript,HTML, CSS 还有很多其它语言的 AST 库,让我们对他有一个感性而直观的认识。<br>请看下图,看看AST语法树长什么样子:</p>
<p><img src="http://img14.360buyimg.com/uba/jfs/t1/8079/13/16060/9637/5c75193fEbfbc926d/b0eb0894b0370be5.png"><br>此图看到的是一个 ExportDefaultDeclaration 也就是export default {},还有他的位置信息,注释失信,tokens等等。</p>
<h6 id="国际惯例,先来一个小demo"><a href="#国际惯例,先来一个小demo" class="headerlink" title="国际惯例,先来一个小demo"></a>国际惯例,先来一个小demo</h6><h6 id="输入数据"><a href="#输入数据" class="headerlink" title="输入数据"></a>输入数据</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">square</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> n * n;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h6 id="处理数据"><a href="#处理数据" class="headerlink" title="处理数据"></a>处理数据</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">astFn() {</span><br><span class="line"> <span class="keyword">const</span> code = <span class="string">`function square(n) {</span></span><br><span class="line"><span class="string"> return n * n;</span></span><br><span class="line"><span class="string"> }`</span>;</span><br><span class="line"> <span class="comment">//得到 AST 语法树</span></span><br><span class="line"> <span class="keyword">const</span> ast = babylon.parse(code);</span><br><span class="line"></span><br><span class="line"> traverse(ast, {</span><br><span class="line"> enter(path) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"enter: "</span> + path.node.type);</span><br><span class="line"> <span class="comment">//如下的语句匹配到 return 中的 n 与参数 n,并且将它替换为 x。</span></span><br><span class="line"> <span class="keyword">if</span> (path.node.type === <span class="string">"Identifier"</span> && path.node.name === <span class="string">"n"</span>) {</span><br><span class="line"> path.node.name = <span class="string">"x"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> generate(ast, {}, code);<span class="comment">//将 AST 转换为代码</span></span><br><span class="line"> <span class="built_in">console</span>.log(generate(ast, {}, code).code );<span class="comment">//打印出转换后的 JavaScript 代码</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 js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">square</span>(<span class="params">x</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> x * x;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>我们看一下我们得到的 AST 树<br><br><img src="http://img14.360buyimg.com/uba/jfs/t26656/262/2307176011/9469/12844ea3/5bfcc13aN0d2bb9a2.png"><br></p>
<h2 id="二、-简单粗暴的版本"><a href="#二、-简单粗暴的版本" class="headerlink" title="二、 简单粗暴的版本"></a>二、 简单粗暴的版本</h2><p>没有使用 AST 将 VUE 组件转换成小程序组件的简易版本介绍<br>下方是两段代码,简单的逻辑,实现思路,匹配目标字符串,替换字符,然后生成文件。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">regs:{<span class="comment">//通过标签匹配来替换对应的小程序支持的标签</span></span><br><span class="line"> toViewStartTags: <span class="regexp">/(<h1)|(<s)|(<em)|(<ul)|(<li)|(<dl)|(<i)|(<span)/g</span>,</span><br><span class="line"> toViewEndTags: <span class="regexp">/(<\/h1>)|(<\/s>)|(<\/em>)|(<\/ul>)|(<\/li>)|(<\/dl>)|(<\/i>)|(<\/span>)/g</span>,</span><br><span class="line"> toBlockStartTags: <span class="regexp">/(<div)|(<p)/g</span>,</span><br><span class="line"> toBlockEndTags: <span class="regexp">/(<\/div>)|(<\/p>)/g</span>,</span><br><span class="line">},</span><br><span class="line">signObj: {<span class="comment">//通过标签查找来分离脚本,模板和CSS</span></span><br><span class="line"> tempStart: <span class="string">'<template>'</span>,</span><br><span class="line"> tempEnd: <span class="string">'</template>'</span>,</span><br><span class="line"> styleStart: <span class="string">'<style scoped>'</span>,</span><br><span class="line"> styleEnd: <span class="string">'</style>'</span>,</span><br><span class="line"> scriptStart: <span class="string">'<script>'</span>,</span><br><span class="line"> scriptEnd: <span class="string">'</script>'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>上方是正则版本的一些模板匹配规则,经过后续的一系列处理把一个 VUE组件处理得到对应的小程序的 WXML ,WXSS,JSON,JS,4个文件。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//文件</span></span><br><span class="line"><span class="keyword">const</span> wxssFilePath = path.join(dirPath, <span class="string">`<span class="subst">${mpFile}</span>.wxss`</span>);</span><br><span class="line"><span class="keyword">const</span> jsFilePath = path.join(dirPath, <span class="string">`<span class="subst">${mpFile}</span>.js`</span>);</span><br><span class="line"><span class="keyword">const</span> wxmlFilePath = path.join(dirPath, <span class="string">`<span class="subst">${mpFile}</span>.wxml`</span>);</span><br><span class="line"><span class="keyword">const</span> jsonFilePath = path.join(dirPath, <span class="string">`<span class="subst">${mpFile}</span>.json`</span>);</span><br><span class="line"><span class="keyword">if</span> (!fs.existsSync(dirPath)) {</span><br><span class="line"> fs.mkdirSync(dirPath);</span><br><span class="line">}</span><br><span class="line">fs.writeFile(wxssFilePath, wxssContent, err => {</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="comment">//console.log(`dist目录下生成${mpFile}.wxss文件成功`);</span></span><br><span class="line"> fs.writeFile(jsFilePath, jsContent, err => {</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="comment">// console.log(`dist目录下生成${mpFile}.js文件成功`);</span></span><br><span class="line"> fs.writeFile(wxmlFilePath, wxmlContent, err => {</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="comment">//console.log(`dist目录下生成${mpFile}.wxml文件成功`);</span></span><br><span class="line"> fs.writeFile(jsonFilePath, jsonContent, err => {</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`dist目录下生成<span class="subst">${mpFile}</span>.json文件成功`</span>)</span><br><span class="line"> resolve(<span class="string">`生成<span class="subst">${mpFile}</span>.json文件成功`</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>上方是处理得到的 WXML ,WXSS,JSON,JS,4个文件,并且生成到对应的目录下。代码实现用时较短,后续更改方案,并没有做优化,这里就不做详细展开讨论这个实现方案了。</p>
<h2 id="三、-AST-三大法宝"><a href="#三、-AST-三大法宝" class="headerlink" title="三、 AST 三大法宝"></a>三、 AST 三大法宝</h2><p>Babel 是 JavaScript 编译器,更确切地说是源码到源码的编译器,通常也叫做“ 转换编译器(transpiler)”。 意思是说你为 Babel 提供一些 JavaScript 代码,Babel 更改这些代码,然后返回给你新生成的代码。</p>
<p>babel-core:Babel 的编译器;它暴露了 babel.transform 方法。</p>
<p>[1] babylon:Babylon 是 Babel 的解析器。用于生成 AST 语法树。</p>
<p>[2] babel-traverse:Babel 的遍历器,所有的transformers都使用该工具遍历所有的 AST (抽象语法树),维护了整棵树的状态,并且负责替换、移除和添加节点。我们可以和 Babylon 一起使用来遍历和更新节点。</p>
<p>[3] babel-generator:Babel 的代码生成器。它读取AST并将其转换为代码。</p>
<p>整个编译器就被分成了三部分:分析器、转换器、生成器,大致的流程是:</p>
<p>输入字符串 -> babylon分析器 parse -> 得到 AST -> 转换器 -> 得到 AST -> babel-generator -> 输出</p>
<h5 id="总结核心三步:"><a href="#总结核心三步:" class="headerlink" title="总结核心三步:"></a>总结核心三步:</h5><p>AST 三大法宝<br>babylon.parse => traverse 转换 AST => generate(ast, {}, code).code) 生成</p>
<p>感兴趣的童鞋,可以在网上或者看参考资料都有介绍,这里篇幅有限,感兴趣我们下回详细展开。</p>
<h2 id="四、-AST-到底怎么转换呢?"><a href="#四、-AST-到底怎么转换呢?" class="headerlink" title="四、 AST 到底怎么转换呢?"></a>四、 AST 到底怎么转换呢?</h2><p>使用 AST 将 VUE 组件转换为小程序组件的部分实现。<br>今天我们主要分析 JavaScript 的部分处理。</p>
<h6 id="转换之前的-VUE-组件代码-Demo"><a href="#转换之前的-VUE-组件代码-Demo" class="headerlink" title="转换之前的 VUE 组件代码 Demo"></a>转换之前的 VUE 组件代码 Demo</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> <span class="comment">//组件的对外属性</span></span><br><span class="line"> props: {</span><br><span class="line"> max: {</span><br><span class="line"> type: <span class="built_in">Number</span>,</span><br><span class="line"> value: <span class="number">99</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"> data(){</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> num:<span class="number">10000</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"> methods: {</span><br><span class="line"> textFn() {</span><br><span class="line"> <span class="keyword">this</span>.num = <span class="number">2</span></span><br><span class="line"> },</span><br><span class="line"> onMyButtonTap: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.num = <span class="number">666</span></span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h6 id="处理后我们得到的小程序组件-JavaScript-部分代码"><a href="#处理后我们得到的小程序组件-JavaScript-部分代码" class="headerlink" title="处理后我们得到的小程序组件 JavaScript 部分代码"></a>处理后我们得到的小程序组件 JavaScript 部分代码</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</span><br><span class="line"> properties: {</span><br><span class="line"> <span class="comment">//组件的对外属性</span></span><br><span class="line"> max: {</span><br><span class="line"> type: <span class="built_in">Number</span>,</span><br><span class="line"> value: <span class="number">99</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"> data(){</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> num: <span class="number">10000</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"> methods: {</span><br><span class="line"> textFn() {</span><br><span class="line"> <span class="keyword">this</span>.setData({</span><br><span class="line"> num: <span class="number">2</span></span><br><span class="line"> });</span><br><span class="line"> },</span><br><span class="line"> onMyButtonTap: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.setData({</span><br><span class="line"> num: <span class="number">666</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>
<h6 id="我们对js动了什么手脚-亦可封装成babel插件"><a href="#我们对js动了什么手脚-亦可封装成babel插件" class="headerlink" title="我们对js动了什么手脚(亦可封装成babel插件):"></a>我们对js动了什么手脚(亦可封装成babel插件):</h6><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//to AST</span></span><br><span class="line"><span class="keyword">const</span> ast = babylon.parse(code, {</span><br><span class="line"> sourceType: <span class="string">"module"</span>,</span><br><span class="line"> plugins: [<span class="string">"flow"</span>]</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//AST 转换 node,nodetype很关键</span></span><br><span class="line">traverse(ast, {</span><br><span class="line"> enter(path) {</span><br><span class="line"> <span class="comment">//打印出node.type</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"enter: "</span> + path.node.type);</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">ObjectProperty(path) {</span><br><span class="line"> <span class="comment">//props 替换为 properties</span></span><br><span class="line"> <span class="keyword">if</span> (path.node.key.name === <span class="string">"props"</span>) {</span><br><span class="line"> path.node.key.name = <span class="string">"properties"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//修改methods中使用到数据属性的方法中。this.prop至this.data.prop等 与 this.setData的处理 感兴趣的童鞋可以尝试一下,代码仅供参考,提供一个思路,实际使用请详细阅读相关的文档来处理。</span></span><br><span class="line">MemberExpression(path) {</span><br><span class="line"> <span class="keyword">let</span> datasVals = datas.map(<span class="function">(<span class="params">item,index</span>) =></span>{</span><br><span class="line"> <span class="keyword">return</span> item.key.name <span class="comment">//拿到data属性中的第一级</span></span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">if</span> (<span class="comment">//含有this的表达式 并且包含data属性</span></span><br><span class="line"> path.node.object.type === <span class="string">"ThisExpression"</span> &&</span><br><span class="line"> datasVals.includes(path.node.property.name)</span><br><span class="line"> ) {</span><br><span class="line"> path.get(<span class="string">"object"</span>).replaceWithSourceString(<span class="string">"this.data"</span>);</span><br><span class="line"> <span class="comment">//判断是不是赋值操作</span></span><br><span class="line"> <span class="keyword">if</span> (</span><br><span class="line"> (t.isAssignmentExpression(path.parentPath) &&</span><br><span class="line"> path.parentPath.get(<span class="string">"left"</span>) === path) ||</span><br><span class="line"> t.isUpdateExpression(path.parentPath)</span><br><span class="line"> ) {</span><br><span class="line"> <span class="keyword">const</span> expressionStatement = path.findParent(<span class="function"><span class="params">parent</span> =></span></span><br><span class="line"> parent.isExpressionStatement()</span><br><span class="line"> );</span><br><span class="line"> <span class="comment">//篇幅有限 省略部分</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h6 id="转换之前的js代码的部分-AST-树:"><a href="#转换之前的js代码的部分-AST-树:" class="headerlink" title="转换之前的js代码的部分 AST 树:"></a>转换之前的js代码的部分 AST 树:</h6><p><img src="http://img12.360buyimg.com/uba/jfs/t30847/232/734714251/110991/96f7e8c6/5bfcb972N9dd3d8cb.png"><br></p>
<h6 id="具体的-API-使用,童鞋们看一下babel相关的文档了解一下,一回生,二回熟嘛。"><a href="#具体的-API-使用,童鞋们看一下babel相关的文档了解一下,一回生,二回熟嘛。" class="headerlink" title="具体的 API 使用,童鞋们看一下babel相关的文档了解一下,一回生,二回熟嘛。"></a>具体的 API 使用,童鞋们看一下babel相关的文档了解一下,一回生,二回熟嘛。</h6><h2 id="五、总结:"><a href="#五、总结:" class="headerlink" title="五、总结:"></a>五、总结:</h2><p>通过以上我们的介绍,我们大概对抽象语法树有了初步的了解。总体思路是:我们用Babel的解析器 把 JavaScript 源码转化为抽象语法树,<br>再通过 Babel 的遍历器遍历 AST (抽象语法树),替换、移除和添加节点,得到一个新的 AST 树。最后, 使用,Babel 的代码生成器 Babel Generator模块 读取 处理后的 AST 并将其转换为代码。任务就完成了!</p>
<p>本文对于 VUE 组件转换为小程序组件的转换部分包括如下内容:</p>
<ol>
<li>VUE 组件对外属性转换为小程序对外属性的处理</li>
<li>VUE 组件内部数据的转换为小程序内部数据的处理</li>
<li>VUE 组件中 methods 中的赋值语句转换为小程序赋值语句的处理简单介绍<br>具体转换过程中涉及到的问题还有很多,有机会再做展开!<br>感谢大家的耐心阅读,希望本文对大家有所帮助,在技术探索的路上,我们一往无前。<br>也欢迎大家关注【全栈探索公众号】,每周都会有技术好文推出!</li>
</ol>
<h2 id="扩展阅读"><a href="#扩展阅读" class="headerlink" title="扩展阅读"></a>扩展阅读</h2><p>[1] <a href="https://astexplorer.net/" target="_blank" rel="noopener">https://astexplorer.net/</a></p>
<p>[2] <a href="https://babeljs.io/docs/en/next/babel-types.html#objectexpression" target="_blank" rel="noopener">https://babeljs.io/docs/en/next/babel-types.html#objectexpression</a> </p>
<p>[3] <a href="https://github.com/babel/babel/tree/6.x/packages/babel-types" target="_blank" rel="noopener">https://github.com/babel/babel/tree/6.x/packages/babel-types</a></p>
<p>[4] <a href="http://esprima.org/demo/parse.html#" target="_blank" rel="noopener">http://esprima.org/demo/parse.html#</a></p>
<p>[5] <a href="https://segmentfault.com/a/1190000014178462?utm_source=tag-newest" target="_blank" rel="noopener">https://segmentfault.com/a/1190000014178462?utm_source=tag-newest</a></p>
<p>[6] <a href="https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E8%AA%9E%E6%B3%95%E6%A8%B9" target="_blank" rel="noopener">https://zh.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E8%AA%9E%E6%B3%95%E6%A8%B9</a></p>
<p>[7] <a href="https://itnext.io/ast-for-javascript-developers-3e79aeb08343" target="_blank" rel="noopener">https://itnext.io/ast-for-javascript-developers-3e79aeb08343</a></p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/05/29/20181204-ast-有魅力的绿巨人-第一回合-丁岳/" data-id="cjw8n3wk00003ucjtainte30m" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-20180709-函数式编程的四特征-丁岳" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/05/29/20180709-函数式编程的四特征-丁岳/" class="article-date">
<time datetime="2019-05-29T02:55:14.255Z" itemprop="datePublished">2019-05-29</time>
</a>
</div>
<div class="article-inner">
<div class="article-entry" itemprop="articleBody">
<h2 id="函数式编程四特征"><a href="#函数式编程四特征" class="headerlink" title="函数式编程四特征"></a>函数式编程四特征</h2><h3 id="本文分为五个部分"><a href="#本文分为五个部分" class="headerlink" title="本文分为五个部分:"></a>本文分为五个部分:</h3><ol>
<li>为什么要用函数式编程</li>
<li>函数式编程的概要</li>
<li>函数式编程的四特征</li>
<li>函数式编程四特征总结</li>
</ol>
<h2 id="一,为什么要用函数式编程"><a href="#一,为什么要用函数式编程" class="headerlink" title="一,为什么要用函数式编程"></a>一,为什么要用函数式编程</h2><h4 id="面向对象的一些特点"><a href="#面向对象的一些特点" class="headerlink" title="面向对象的一些特点"></a>面向对象的一些特点</h4><ol>
<li>某些场景代码充斥着无数的for循环,以及无数的分支判断语句,使代码的可读性,健壮性降低</li>
<li>某些场景代码定位问题比较困难,因为某些方法的每次调用输出都不一样,并且会改变输入数据,(数据经常被一些不纯函数无意间修改)导致代码出错率增加</li>
<li>函数使用不够灵活,不能自由组合,复用率低</li>
</ol>
<h2 id="二,函数式编程概要"><a href="#二,函数式编程概要" class="headerlink" title="二,函数式编程概要"></a>二,函数式编程概要</h2><h5 id="注:本文用到的函数式库是ramda,类似函数式库有underscore-lodash等"><a href="#注:本文用到的函数式库是ramda,类似函数式库有underscore-lodash等" class="headerlink" title="注:本文用到的函数式库是ramda,类似函数式库有underscore,lodash等"></a>注:本文用到的函数式库是ramda,类似函数式库有underscore,lodash等</h5><h5 id="通过此图我们先大概看一下函数式编程的函数组合的特性"><a href="#通过此图我们先大概看一下函数式编程的函数组合的特性" class="headerlink" title="通过此图我们先大概看一下函数式编程的函数组合的特性"></a>通过此图我们先大概看一下函数式编程的函数组合的特性</h5><p><img src="http://img13.360buyimg.com/uba/jfs/t23179/6/816638067/51763/f04fc636/5b433572N0e6e047f.png" width="500"></p>
<h4 id="接下来我们具体看一下函数式编程"><a href="#接下来我们具体看一下函数式编程" class="headerlink" title="接下来我们具体看一下函数式编程"></a>接下来我们具体看一下函数式编程</h4><p><img src="http://img13.360buyimg.com/uba/jfs/t24070/107/818158903/39897/364c7189/5b4335cfNf10ff2ca.png" width="500"></p>
<h3 id="函数式思想-λ演算"><a href="#函数式思想-λ演算" class="headerlink" title="函数式思想-λ演算"></a>函数式思想-<a href="https://baike.baidu.com/item/%CE%BB%E6%BC%94%E7%AE%97/8019133?fr=aladdin" target="_blank" rel="noopener">λ演算</a></h3><blockquote>
<p>函数式思想展现的是一种纯粹的数学思维。来源于Lambda演算 ( λ ) (演算是一套用于研究函数定义、函数应用和递归的形式系统)<br>函数并不代表任何物质(对象,相对于面向对象思想而言),而它仅仅代<br>表一种针对数据的转换行为。它们是对行为的最高抽象,具有非凡<br>的抽象能力和表现力。函数式编程, 就是用函数(计算)来表示程序, 用函数(计算)的组合来表达程序的组合的思维方式.</p>
</blockquote>
<h3 id="函数式编程的本质"><a href="#函数式编程的本质" class="headerlink" title="函数式编程的本质"></a>函数式编程的本质</h3><ol>
<li>编程任务 (一个或多个这样的任务,就组成了程序)</li>
</ol>
<h4 id="编程主要就是写中间的那部分运算逻辑,如图:"><a href="#编程主要就是写中间的那部分运算逻辑,如图:" class="headerlink" title="编程主要就是写中间的那部分运算逻辑,如图:"></a>编程主要就是写中间的那部分运算逻辑,如图:</h4><p><img src="http://img12.360buyimg.com/uba/jfs/t23449/309/812084269/26634/8aa2d77e/5b43369fN01e7b462.png" width="500"></p>
<blockquote>
<p>现在,主流写法是过程式编程和面向对象编程,还有擅长纯运算函数式编程(强调将计算过程分解成可复用的函数)</p>
</blockquote>
<ol start="2">
<li>我们可以把整个运算过程,想象成一根水管(pipe),数据从这头进去,那头出来,如图:</li>
</ol>
<p><img src="http://img11.360buyimg.com/uba/jfs/t22426/268/2021045919/21601/33cab070/5b433b40N572d6b4f.png" width="500"></p>
<h4 id="函数式编程思维-就是用函数-计算-来表示程序-用函数-计算-的组合来表达程序的组合的思维方式"><a href="#函数式编程思维-就是用函数-计算-来表示程序-用函数-计算-的组合来表达程序的组合的思维方式" class="headerlink" title="函数式编程思维, 就是用函数(计算)来表示程序, 用函数(计算)的组合来表达程序的组合的思维方式."></a>函数式编程思维, 就是用函数(计算)来表示程序, 用函数(计算)的组合来表达程序的组合的思维方式.</h4><h2 id="三,函数式编程的四特征"><a href="#三,函数式编程的四特征" class="headerlink" title="三,函数式编程的四特征"></a>三,函数式编程的四特征</h2><h3 id="FP特点一-纯函数"><a href="#FP特点一-纯函数" class="headerlink" title="FP特点一 纯函数"></a>FP特点一 纯函数</h3><h4 id="纯函数是指只处理传入的参数。"><a href="#纯函数是指只处理传入的参数。" class="headerlink" title="纯函数是指只处理传入的参数。"></a>纯函数是指只处理传入的参数。</h4><ol>
<li>一定有返回值(返回一个新的值,不改变原始数据)</li>
<li>至少传入一个参数</li>
<li>没有副作用,输入相同,保证输出相同(幂等函数)</li>
</ol>
<h4 id="javascript内置函数有不少纯函数,也有不少非纯函数。"><a href="#javascript内置函数有不少纯函数,也有不少非纯函数。" class="headerlink" title="javascript内置函数有不少纯函数,也有不少非纯函数。"></a>javascript内置函数有不少纯函数,也有不少非纯函数。</h4><h5 id="纯函数:"><a href="#纯函数:" class="headerlink" title="纯函数:"></a>纯函数:</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span> <span class="built_in">Array</span>.prototype.slice</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> <span class="built_in">Array</span>.prototype.map</span><br><span class="line"></span><br><span class="line"><span class="number">3.</span> <span class="built_in">String</span>.prototype.toUpperCase</span><br></pre></td></tr></table></figure>
<h5 id="非纯函数:"><a href="#非纯函数:" class="headerlink" title="非纯函数:"></a>非纯函数:</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span> <span class="built_in">Math</span>.random</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> <span class="built_in">Date</span>.now</span><br><span class="line"></span><br><span class="line"><span class="number">3.</span> <span class="built_in">Array</span>.ptototype.splice</span><br></pre></td></tr></table></figure>
<h4 id="我们看一下非纯函数-splice-和-纯函数-slice"><a href="#我们看一下非纯函数-splice-和-纯函数-slice" class="headerlink" title="我们看一下非纯函数 splice 和 纯函数 slice"></a>我们看一下非纯函数 splice 和 纯函数 slice</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//splice 是典型的非纯函数,每次的返回值不一次,而且会把输入数据改变</span></span><br><span class="line"><span class="keyword">var</span> r1 = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>];</span><br><span class="line">r1.splice(<span class="number">0</span>,<span class="number">3</span>);<span class="comment">// 输出[1,2,3] 此时r1为[4, 5, 6, 7]</span></span><br><span class="line">r1.splice(<span class="number">0</span>,<span class="number">3</span>);<span class="comment">// 输出[4, 5, 6] 此时r1为[7]</span></span><br><span class="line">r1.splice(<span class="number">0</span>,<span class="number">3</span>);<span class="comment">// 输出[7] 此时r1为[]</span></span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//slice 是纯函数,每次的返回值都一样,而且会把输入数据不会被改变</span></span><br><span class="line"><span class="keyword">var</span> r=[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>]</span><br><span class="line">r.slice(<span class="number">0</span>,<span class="number">3</span>);<span class="comment">//输出 [1, 2, 3] 此时 r 为 [1, 2, 3, 4]</span></span><br><span class="line">r.slice(<span class="number">0</span>,<span class="number">3</span>);<span class="comment">//输出 [1, 2, 3] 此时 r 为 [1, 2, 3, 4]</span></span><br><span class="line">r.slice(<span class="number">0</span>,<span class="number">3</span>);<span class="comment">//输出 [1, 2, 3] 此时 r 为 [1, 2, 3, 4]</span></span><br></pre></td></tr></table></figure>
<h4 id="这就是我们强调使用纯函数的原因,因为纯函数相对于非纯函数来说,在可缓存性、可移植性、可测试性以及并行计算方面都有着巨大的优势。"><a href="#这就是我们强调使用纯函数的原因,因为纯函数相对于非纯函数来说,在可缓存性、可移植性、可测试性以及并行计算方面都有着巨大的优势。" class="headerlink" title="这就是我们强调使用纯函数的原因,因为纯函数相对于非纯函数来说,在可缓存性、可移植性、可测试性以及并行计算方面都有着巨大的优势。"></a>这就是我们强调使用纯函数的原因,因为纯函数相对于非纯函数来说,在可缓存性、可移植性、可测试性以及并行计算方面都有着巨大的优势。</h4><h3 id="FP特点二-不可变"><a href="#FP特点二-不可变" class="headerlink" title="FP特点二 不可变"></a>FP特点二 不可变</h3><h4 id="函数式编程中-不可变数据使代码更简单和安全"><a href="#函数式编程中-不可变数据使代码更简单和安全" class="headerlink" title="函数式编程中 (不可变数据使代码更简单和安全)"></a>函数式编程中 (不可变数据使代码更简单和安全)</h4><ol>
<li>没有变量 由于历史原因存储的值仍然叫变量,但他们是不可变的。比如说,一旦 x 中存了一个值,它的生命周期中就一直是这个值;</li>
<li>没有循环 通过递归来做循环 非递归的循环更容易出问题,因为需要使用可变变量</li>
</ol>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//通过for来求和,使用可变变量i,是存在一定风险的。</span></span><br><span class="line"><span class="keyword">var</span> acc = <span class="number">0</span>; </span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <=<span class="number">10</span>; ++i){ </span><br><span class="line"> acc += i; </span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(acc);<span class="comment">//55</span></span><br></pre></td></tr></table></figure>
<h4 id="当然我们也可以使用let来规避,但其实也是存在风险的"><a href="#当然我们也可以使用let来规避,但其实也是存在风险的" class="headerlink" title="当然我们也可以使用let来规避,但其实也是存在风险的"></a>当然我们也可以使用let来规避,但其实也是存在风险的</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">var</span> acc = <span class="number">0</span>; </span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i1 = <span class="number">1</span>; i1 <=<span class="number">10</span>; ++i1){ i1=<span class="number">111</span></span><br><span class="line"> acc += i1; </span><br><span class="line">}</span><br><span class="line"><span class="built_in">console</span>.log(acc);<span class="comment">//111</span></span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//函数式当中递归的处理方式,很显然,此函数只要输入相同,输出就一定相同,不存在可变变量。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sumRange</span>(<span class="params">start, end, acc</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (start > end) <span class="keyword">return</span> acc; </span><br><span class="line"> <span class="keyword">return</span> sumRange(start + <span class="number">1</span>, end, acc + start) </span><br><span class="line">} </span><br><span class="line"><span class="built_in">console</span>.log(sumRange(<span class="number">1</span>, <span class="number">10</span>, <span class="number">0</span>));<span class="comment">//55</span></span><br></pre></td></tr></table></figure>
<h4 id="还有一种不错的,用js内置的reduce来处理,看上去好了很多。这是js趋向于函数式的一种体现。"><a href="#还有一种不错的,用js内置的reduce来处理,看上去好了很多。这是js趋向于函数式的一种体现。" class="headerlink" title="还有一种不错的,用js内置的reduce来处理,看上去好了很多。这是js趋向于函数式的一种体现。"></a>还有一种不错的,用js内置的reduce来处理,看上去好了很多。这是js趋向于函数式的一种体现。</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>,<span class="number">10</span>]; </span><br><span class="line"><span class="comment">//reduce 数组成员依次执行指定函数,每一次的运算结果都会进入一个累积变量。</span></span><br><span class="line">arr.reduce(<span class="function"><span class="keyword">function</span> (<span class="params">preValue,curValue,index,array</span>) </span>{ </span><br><span class="line"> <span class="keyword">return</span> preValue + curValue; </span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<h4 id="那么,我们用ramdajs中的reduce来处理一下"><a href="#那么,我们用ramdajs中的reduce来处理一下" class="headerlink" title="那么,我们用ramdajs中的reduce来处理一下"></a>那么,我们用ramdajs中的reduce来处理一下</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> add = <span class="function"><span class="keyword">function</span> (<span class="params">a, b</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> a + b;</span><br><span class="line">};</span><br><span class="line">R.reduce(add, <span class="number">0</span>)(R.range(<span class="number">0</span>,<span class="number">11</span>)) <span class="comment">// 55</span></span><br><span class="line"><span class="comment">//R.range(0,11) 输出为 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]</span></span><br></pre></td></tr></table></figure>
<h3 id="FP特点三-柯里化"><a href="#FP特点三-柯里化" class="headerlink" title="FP特点三 柯里化"></a>FP特点三 柯里化</h3><p>柯里化(又称部分求值),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数(多参转为单参函数)</p>
<h5 id="举栗子"><a href="#举栗子" class="headerlink" title="举栗子"></a>举栗子</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> addFourNumbers = <span class="function">(<span class="params">a, b, c, d</span>) =></span></span><br><span class="line">a + b + c + d;</span><br><span class="line"><span class="keyword">var</span> curriedAddFourNumbers = R.curry(addFourNumbers);<span class="comment">//通过R.curry对普通函数进行柯里化</span></span><br><span class="line"><span class="keyword">var</span> f = curriedAddFourNumbers(<span class="number">1</span>, <span class="number">2</span>);<span class="comment">//先传入部分参数,直到参数传齐才求值,惰性求值</span></span><br><span class="line"><span class="keyword">var</span> g = f(<span class="number">3</span>);</span><br><span class="line">g(<span class="number">4</span>) <span class="comment">// 10</span></span><br></pre></td></tr></table></figure>
<p>map我们经常使用,但是,我们有什么理由用函数式里的map呢<br>首先,看一下map的基本使用 </p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> double = <span class="function"><span class="params">x</span> =></span> x * <span class="number">2</span>;</span><br><span class="line"><span class="keyword">var</span> oneParam = R.map(double); </span><br><span class="line">oneParam([<span class="number">2</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>]) <span class="comment">//[4, 8, 10, 12, 14]</span></span><br><span class="line">oneParam({<span class="attr">x</span>: <span class="number">1</span>, <span class="attr">y</span>: <span class="number">2</span>, <span class="attr">z</span>: <span class="number">3</span>}) <span class="comment">//{x: 2, y: 4, z: 6}</span></span><br><span class="line"><span class="comment">//和js原型上的map的区别是 数据的接收方式不同,与R.map是柯里化后的。</span></span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> users = [</span><br><span class="line">{<span class="attr">name</span>: <span class="string">'chet'</span>, <span class="attr">age</span>:<span class="number">25</span>}, </span><br><span class="line">{<span class="attr">name</span>:<span class="string">'joe'</span>, <span class="attr">age</span>:<span class="number">24</span>}</span><br><span class="line">];</span><br><span class="line"><span class="keyword">var</span> a =R.pipe(</span><br><span class="line"> R.sortBy(R.prop(<span class="string">'age'</span>)), <span class="comment">//根据age的属性值来排序,输出排序后的数组对象 输出[{name:'joe', age:24},{name: 'chet', age:25}] </span></span><br><span class="line"> R.map(R.prop(<span class="string">'name'</span>)), <span class="comment">// 输入 [{name:'joe', age:24},{name: 'chet', age:25}] 输出["joe", "chet"]</span></span><br><span class="line"> R.join(<span class="string">','</span>) ); <span class="comment">//相当于 R.join(',')(["joe", "chet"]) </span></span><br><span class="line"> a(users) <span class="comment">// 最终得到 "joe,chet"</span></span><br></pre></td></tr></table></figure>
<h3 id="FP特点四-高阶函数"><a href="#FP特点四-高阶函数" class="headerlink" title="FP特点四 高阶函数"></a>FP特点四 高阶函数</h3><p>函数可以作为参数被传递;函数可以作为返回值输出。<br>将复用的粒度降低到函数级别,相对于面向对象语言,复用的粒度更低。</p>
<h5 id="举栗子-1"><a href="#举栗子-1" class="headerlink" title="举栗子"></a>举栗子</h5><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">compose (将多个函数合并成一个函数,从右到左执行)</span><br><span class="line">R.compose(<span class="built_in">Math</span>.abs, R.add(<span class="number">1</span>), R.multiply(<span class="number">2</span>))(<span class="number">-4</span>) <span class="comment">// 7</span></span><br><span class="line"><span class="comment">//R.multiply(2) 输出-8</span></span><br><span class="line"><span class="comment">//R.add(1) 输入-8, 输出-7</span></span><br><span class="line"><span class="comment">//Math.abs 输入-7, 输出7</span></span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">pipe(将多个函数合并成一个函数,从左到右执行)</span><br><span class="line"><span class="keyword">var</span> negative = <span class="function"><span class="params">x</span> =></span> <span class="number">-1</span> * x;</span><br><span class="line"><span class="keyword">var</span> increaseOne = <span class="function"><span class="params">x</span> =></span> x + <span class="number">1</span>;</span><br><span class="line"><span class="keyword">var</span> f = R.pipe(<span class="built_in">Math</span>.pow, negative, increaseOne);</span><br><span class="line">f(<span class="number">3</span>, <span class="number">4</span>) <span class="comment">// -80 => -(3^4) + 1</span></span><br><span class="line"><span class="comment">// Math.pow 输入 3和4,输出 81</span></span><br><span class="line"><span class="comment">// negative 输入 81, 输出 -81</span></span><br><span class="line"><span class="comment">// increaseOne 输入 -81, 输出 -80</span></span><br></pre></td></tr></table></figure>
<h3 id="函数式库在mvvm框架中的使用"><a href="#函数式库在mvvm框架中的使用" class="headerlink" title="函数式库在mvvm框架中的使用"></a>函数式库在mvvm框架中的使用</h3><p>####只要是处理数据,我们随时可以使用ramdajs这样的函数式库来处理 </p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">created(){</span><br><span class="line"> indexApi.getFloorsData(<span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> data = (<span class="keyword">typeof</span>(res) === <span class="string">'string'</span> ? <span class="built_in">JSON</span>.parse(res) : res);</span><br><span class="line"> <span class="keyword">if</span>(data.code == <span class="number">200</span>) {</span><br><span class="line"> <span class="keyword">this</span>.floorData = data; </span><br><span class="line"> <span class="keyword">let</span> errorImg = commonImg.state.placeholderImgFifth;</span><br><span class="line"> <span class="keyword">let</span> noObj = <span class="function">(<span class="params">item</span>) =></span> R.type(item.imgurl) != <span class="string">"Object"</span>;</span><br><span class="line"> <span class="keyword">let</span> addProp = <span class="function">(<span class="params">item</span>) =></span> R.merge(item)({<span class="string">'imgurl'</span>: {<span class="attr">src</span>: item.imgurl,<span class="attr">error</span>: errorImg,<span class="attr">loading</span>: errorImg}});</span><br><span class="line"> <span class="keyword">let</span> addImgProp = R.pipe(R.filter(noObj),R.map(addProp));</span><br><span class="line"> <span class="keyword">let</span> baseItems = R.pipe(R.take(<span class="number">10</span>),R.pluck(<span class="string">'baseItems'</span>))(<span class="keyword">this</span>.floorData.floorMenuItems);<span class="comment">//所有楼层数据 </span></span><br><span class="line"> <span class="keyword">let</span> baseItemsAddImgList = R.map(addImgProp)(baseItems);<span class="comment">//增加默认图对象</span></span><br><span class="line"> <span class="keyword">let</span> hasProdAndSkuNo = <span class="function">(<span class="params">item</span>) =></span> R.has(<span class="string">'productId'</span>)(item) && R.has(<span class="string">'skuNo'</span>)(item); </span><br><span class="line"> <span class="keyword">var</span> prodIdSkuIdList = <span class="function">(<span class="params">item</span>) =></span> R.assoc(<span class="string">"prodIdSkuId"</span>,R.prop(<span class="string">'productId'</span>)(item) + <span class="string">'-'</span> + R.prop(<span class="string">'skuNo'</span>)(item))(item);</span><br><span class="line"> <span class="keyword">let</span> mapPlusIds = R.pipe(R.filter(hasProdAndSkuNo),R.map(prodIdSkuIdList));</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> baseItemsImgsAndproIdAndSkuNoList = R.map(mapPlusIds)(baseItemsAddImgList);<span class="comment">//写入属性prodIdSkuId到baseItems中</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> proIdAndSkuNoListList = R.pipe(R.map(R.pluck(<span class="string">'prodIdSkuId'</span>)),R.join(<span class="string">','</span>))(baseItemsImgsAndproIdAndSkuNoList);<span class="comment">//得到productId-skuNo串</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">this</span>.floorData.floorMenuItems =R.zipWith(R.assoc(<span class="string">'baseItems'</span>),baseItemsImgsAndproIdAndSkuNoList,<span class="keyword">this</span>.floorData.floorMenuItems);<span class="comment">//数据组合</span></span><br><span class="line"> }</span><br><span class="line"> indexApi.getPrice(proIdAndSkuNoListList, (priceRes) => {</span><br><span class="line"> <span class="keyword">let</span> priceData = <span class="keyword">typeof</span>(priceRes) === <span class="string">'string'</span> ? <span class="built_in">JSON</span>.parse(priceRes) : priceRes;</span><br><span class="line"> <span class="keyword">if</span>(R.prop(<span class="string">'code'</span>)(priceData.result) == <span class="number">200</span>) {</span><br><span class="line"> R.flatten(baseItemsImgsAndproIdAndSkuNoList).map(<span class="function"><span class="keyword">function</span>(<span class="params">v,index</span>)</span>{;</span><br><span class="line"> <span class="built_in">Object</span>.keys(priceData.data).map(<span class="function">(<span class="params">pidSid</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span>(R.equals(pidSid,v.prodIdSkuId)){</span><br><span class="line"> <span class="keyword">this</span>.$<span class="keyword">set</span>(v,'newPrice',priceData.data[pidSid]['price'].pcPrice); </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><span class="line"> }, (error) => {</span><br><span class="line"> <span class="built_in">console</span>.log(error);</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="四,函数式编程四特征总结"><a href="#四,函数式编程四特征总结" class="headerlink" title="四,函数式编程四特征总结"></a>四,函数式编程四特征总结</h2><ol>
<li>纯函数 也称幂等函数 (纯函数,没有“副作用”)</li>
<li>不可变 (使代码更简单和安全)</li>
<li>柯里化 (多参函数转为可接收单参数函数)</li>
<li>高阶函数 (使函数的复用粒度更低)</li>
</ol>
<h4 id="通过-柯里化-高阶函数-可以得到接近自然语言的表达"><a href="#通过-柯里化-高阶函数-可以得到接近自然语言的表达" class="headerlink" title="通过 柯里化 , 高阶函数 可以得到接近自然语言的表达"></a>通过 <code>柯里化</code> , <code>高阶函数</code> 可以得到接近自然语言的表达</h4><h4 id="当下流行的vue-react也都提供了函数式组件,使用函数式组件最大的好处就是它能够将容器型和展示型组件明确区分开来,避免产生大型以及杂乱的组件。没有-this-关键字也就意味着没有快捷方式在整个应用中随机地展开状态。"><a href="#当下流行的vue-react也都提供了函数式组件,使用函数式组件最大的好处就是它能够将容器型和展示型组件明确区分开来,避免产生大型以及杂乱的组件。没有-this-关键字也就意味着没有快捷方式在整个应用中随机地展开状态。" class="headerlink" title="当下流行的vue,react也都提供了函数式组件,使用函数式组件最大的好处就是它能够将容器型和展示型组件明确区分开来,避免产生大型以及杂乱的组件。没有 this 关键字也就意味着没有快捷方式在整个应用中随机地展开状态。"></a>当下流行的vue,react也都提供了函数式组件,使用函数式组件最大的好处就是它能够将容器型和展示型组件明确区分开来,避免产生大型以及杂乱的组件。没有 this 关键字也就意味着没有快捷方式在整个应用中随机地展开状态。</h4><h2 id="五,常见的函数式编程语言"><a href="#五,常见的函数式编程语言" class="headerlink" title="五,常见的函数式编程语言"></a>五,常见的函数式编程语言</h2><p>###比较纯粹的函数式编程语言</p>
<table>
<thead>
<tr>
<th>比较纯粹的函数式编程语言</th>
<th style="text-align:center">支持函数式的编程语言</th>
</tr>
</thead>
<tbody>
<tr>
<td>Clojure</td>
<td style="text-align:center">C++</td>
<td></td>
</tr>
<tr>
<td>Erlang</td>
<td style="text-align:center">Java</td>
<td></td>
</tr>
<tr>
<td>Haskell</td>
<td style="text-align:center">javascript</td>
<td></td>
</tr>
<tr>
<td>Lisp</td>
<td style="text-align:center">php</td>
<td></td>
</tr>
<tr>
<td>Perl</td>
<td style="text-align:center">python</td>
<td></td>
</tr>
<tr>
<td>Schema</td>
<td style="text-align:center">ruby</td>
<td></td>
</tr>
<tr>
<td>Scala</td>
<td style="text-align:center">…</td>
<td></td>
</tr>
<tr>
<td>elm</td>
<td style="text-align:center"></td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="五,参考资料"><a href="#五,参考资料" class="headerlink" title="五,参考资料"></a>五,参考资料</h2><ol>
<li><a href="https://baike.baidu.com/item/%CE%BB%E6%BC%94%E7%AE%97/8019133?fr=aladdin" target="_blank" rel="noopener">https://baike.baidu.com/item/%CE%BB%E6%BC%94%E7%AE%97/8019133?fr=aladdin</a></li>
<li><a href="https://ramdajs.com/" target="_blank" rel="noopener">https://ramdajs.com/</a></li>
<li><a href="https://baike.baidu.com/item/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/4035031?fr=aladdin" target="_blank" rel="noopener">https://baike.baidu.com/item/%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B/4035031?fr=aladdin</a></li>
<li>文中的代码片段可以直接引用ramdajs来运行。</li>
</ol>
<h3 id="最后,感谢您的耐心阅读,欢迎沟通,如有问题,欢迎指正。"><a href="#最后,感谢您的耐心阅读,欢迎沟通,如有问题,欢迎指正。" class="headerlink" title="最后,感谢您的耐心阅读,欢迎沟通,如有问题,欢迎指正。"></a>最后,感谢您的耐心阅读,欢迎沟通,如有问题,欢迎指正。</h3>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/05/29/20180709-函数式编程的四特征-丁岳/" data-id="cjw8n3wjj0000ucjtww6bbueb" class="article-share-link">Share</a>
</footer>
</div>
</article>
</section>
<aside id="sidebar">
<div class="widget-wrap">
<h3 class="widget-title">Archives</h3>
<div class="widget">
<ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/05/">May 2019</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Recent Posts</h3>
<div class="widget">
<ul>
<li>
<a href="/2019/05/29/ast-有魅力的绿巨人-第二回合-丁岳/">(no title)</a>
</li>
<li>
<a href="/2019/05/29/20181204-使用AST将VUE组件转换为微信小程序组件-丁岳/">(no title)</a>
</li>
<li>
<a href="/2019/05/29/20181204-ast-有魅力的绿巨人-第一回合-丁岳/">(no title)</a>
</li>
<li>
<a href="/2019/05/29/20180709-函数式编程的四特征-丁岳/">(no title)</a>
</li>
</ul>
</div>
</div>
</aside>
</div>
<footer id="footer">
<div class="outer">
<div id="footer-info" class="inner">
© 2019 John Doe<br>
Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
</div>
</div>
</footer>
</div>
<nav id="mobile-nav">
<a href="/" class="mobile-nav-link">Home</a>
<a href="/archives" class="mobile-nav-link">Archives</a>
</nav>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script src="/js/script.js"></script>
</div>
</body>
</html>