-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
385 lines (206 loc) · 159 KB
/
atom.xml
File metadata and controls
385 lines (206 loc) · 159 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>codingfishman</title>
<subtitle>细雨湿衣看不见,闲花落地听无声</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://codingfishman.github.io/"/>
<updated>2017-03-12T03:42:42.000Z</updated>
<id>http://codingfishman.github.io/</id>
<author>
<name>Fishman Wang</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>从“点赞”到“框架”,发现业务本身的价值</title>
<link href="http://codingfishman.github.io/2017/03/11/%E6%8A%80%E6%9C%AF%E4%B9%8B%E6%A2%A6%E4%B8%8E%E4%B8%9A%E5%8A%A1%E4%B9%8B%E5%AE%9E/"/>
<id>http://codingfishman.github.io/2017/03/11/技术之梦与业务之实/</id>
<published>2017-03-11T09:07:27.000Z</published>
<updated>2017-03-12T03:42:42.000Z</updated>
<content type="html"><![CDATA[<p>假如眼下有两个任务摆在你面前,做一个“点赞”的功能,以及“搭建一个底层框架给开发人员”,你会选哪一个?也许答案不言自明,但是今天我想说,他们都是很好的选择,取决于我们如何对待。</p><p>每位技术人都有一个技术梦,技术从某种程度上定位了我们的“价值”。我也仰慕技术大牛,总是暗自感叹:这太厉害了,他是怎么做到的?我真想达到他的哪怕二分之一呀!这时候我会不带犹豫地选择“框架”的开发。但是从事底层框架的开发总的来说是不需要所有技术人都参与的,相信多数技术人工作时的主要内容,都是服务“点赞”这种客户,那它是不是就那么的不值呢?我觉得不是,因为“点赞”是给我们最终的用户来用的,换句话说,它是我们真正的业务,是我们公司的立脚点。</p><p>而我们参与了这个过程,学习了这个过程,累计了这个过程,唯有技术和这个过程结合,我们的技术才有意义,从这个角度上来说,这些一个个功能,其实也就是我们公司业务的“框架”。</p><p>而这种框架也是可以重复的,也是一种能力,尤其是在业务本身也很专业的时候。我们每一个细节的功能,其实就是整个业务大图的一部分,而与业务结合的技术者,其实是不多的。</p><p>当我们对这些业务足够熟悉,每次参与新的需求讨论,我们已经可以对这些需求进行补充了。而且我们可以从技术的角度上,比如,我们知道最近有某种新的功能,正好可以结合业务来使用,这样一来,我们对业务体验本身也进行了改进。</p><p>所有的业务都有业务本身想解决的痛点,这也是业务存在的价值之一,而这些痛点的本身也有重复的地方,这时候我觉得,一个适用于这个业务的框架是时候构思起来了。正如一开始的框架,是给开发人员来开发代码,解决了开发的痛点,业务的框架也是给客户来用的,解决业务的痛点,价值一点也不少。</p>]]></content>
<summary type="html">
<p>假如眼下有两个任务摆在你面前,做一个“点赞”的功能,以及“搭建一个底层框架给开发人员”,你会选哪一个?也许答案不言自明,但是今天我想说,他们都是很好的选择,取决于我们如何对待。</p>
<p>每位技术人都有一个技术梦,技术从某种程度上定位了我们的“价值”。我也仰慕技术大牛,
</summary>
</entry>
<entry>
<title>ES6的yield是如何执行和接受参数的</title>
<link href="http://codingfishman.github.io/2016/06/17/ES6%20%E7%9A%84yield%E6%98%AF%E5%A6%82%E4%BD%95%E6%89%A7%E8%A1%8C%E5%92%8C%E6%8E%A5%E5%8F%97%E5%8F%82%E6%95%B0%E7%9A%84/"/>
<id>http://codingfishman.github.io/2016/06/17/ES6 的yield是如何执行和接受参数的/</id>
<published>2016-06-17T14:22:47.000Z</published>
<updated>2016-07-10T16:23:50.000Z</updated>
<content type="html"><![CDATA[<p>其实是在去年的JsConf上才第一次看到有人介绍ES5的generator,以及他的实现语法 <code>yield</code>。当时演讲嘉宾举了一个例子,通过<code>next()</code>去调用,获取返回值,并传入参数,我看得略云里雾里,并且基于我的云里雾里,一时没有领会到这个语法的意思。我们先回顾一下这个经常被用来演示yield语法的函数例子</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="function"><span class="keyword">function</span>* <span class="title">gen</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">5</span>; i++) {</span><br><span class="line"> <span class="keyword">yield</span> i</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> g = gen();</span><br><span class="line">g.next() <span class="comment">// {value: 0, done:false}</span></span><br><span class="line">g.next() <span class="comment">// {value: 1, done:false}</span></span><br><span class="line">g.next() <span class="comment">// {value: 2, done:false}</span></span><br><span class="line">g.next() <span class="comment">// {value: 3, done:false}</span></span><br><span class="line">g.next() <span class="comment">// {value: 4, done:false}</span></span><br><span class="line">g.next() <span class="comment">// {value: undefined, done:true}</span></span><br></pre></td></tr></table></figure><p>哎哟,不错哦?这看起来还挺炫的嘛,next()一次执行一次诶!这个语法有意思。随即,我们来到了一个稍微复杂一点的例子:</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="function"><span class="keyword">function</span> * <span class="title">gen</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'执行 A'</span>)</span><br><span class="line"> <span class="keyword">let</span> x = <span class="keyword">yield</span> <span class="number">1</span></span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'执行 B, X is :'</span> + x)</span><br><span class="line"> <span class="keyword">let</span> y = x + (<span class="keyword">yield</span> <span class="number">5</span>)</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'执行 C'</span>)</span><br><span class="line"> <span class="keyword">yield</span> y</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'执行 D'</span>)</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'Y is :'</span> + y);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> g = gen()</span><br><span class="line">g.next()</span><br><span class="line"><span class="comment">// >> 执行 A</span></span><br><span class="line"><span class="comment">// >> {value: 1, done:false}</span></span><br><span class="line">g.next(<span class="number">2</span>)</span><br><span class="line"><span class="comment">// >> 执行B, X is : 2</span></span><br><span class="line"><span class="comment">// >> {value: 5, done:false}</span></span><br><span class="line">g.next(<span class="number">3</span>)</span><br><span class="line"><span class="comment">// >> 执行 C</span></span><br><span class="line"><span class="comment">// >> {value: 5, done:false}</span></span><br><span class="line">g.next(<span class="number">5</span>)</span><br><span class="line"><span class="comment">// >> 执行 D</span></span><br><span class="line"><span class="comment">// >> Y is :5</span></span><br><span class="line"><span class="comment">// >> {value: undefined, done:true}</span></span><br></pre></td></tr></table></figure><p>开始有点云里绕的感觉了,<code>g.next(2)</code> 做了什么?我们知道答案:<code>g.next(2)</code>给yield传入了值。也就是说yield不仅可以将值传出,还可以接受从外面传入的值,这里面其实有一个比较混淆的地方,yiele从值是从哪一步传入的?传之后的函数执行实际上张什么样子呢?我们一步步地看看:</p><h3 id="1-函数调用:-const-g-gen"><a href="#1-函数调用:-const-g-gen" class="headerlink" title="1. 函数调用: const g = gen()"></a>1. 函数调用: <code>const g = gen()</code></h3><p>调用函数<code>gen</code>,并获得了执行 yield 的句柄,但是函数本身并没有被执行。我们可以看到,在第一个<code>g.next()</code>执行以前,<code>console.info('执行 A')</code>并没有执行,因此函数处于待执行状态。</p><h3 id="2-第一个next:-g-next"><a href="#2-第一个next:-g-next" class="headerlink" title="2. 第一个next: g.next()"></a>2. 第一个next: <code>g.next()</code></h3><p>开始执行函数了,且执行到第一个yield语句刚刚结束,同时获取yield语句的返回值。因此我们看到第一个console语句执行了,且得到了第一个yield的返回值,然后停下来。</p><blockquote><p>But wait! 什么叫“yield语句的返回值”? 我们知道 <code>yiled 1</code> 会返回1, 那如果yield后面是一个表达式呢,比如↓↓:</p></blockquote> <figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">let x = <span class="number">1</span> + yield (<span class="number">2</span> + <span class="number">3</span>) + <span class="number">4</span></span><br></pre></td></tr></table></figure><p>答案是:<em>语法错误~~</em>, 但这并不能说明yield错了。。错的是解析器(解析器→_→ 怪我咯?),我们换个写法:</p><figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">let x = <span class="number">1</span> + (yield (<span class="number">2</span> + <span class="number">3</span> )) + <span class="number">4</span></span><br></pre></td></tr></table></figure><p>这样就可以了,答案我想大家也猜到了,那就是会返回5,因为这就是后面表达式的返回值。</p><h3 id="3-第二个next:-g-next-2"><a href="#3-第二个next:-g-next-2" class="headerlink" title="3. 第二个next: g.next(2)"></a>3. 第二个next: <code>g.next(2)</code></h3><p> 这次我们不仅执行了next(),而且传入了参数2。从后面的打印语句,我们可以看到这个2给了 <code>X</code> 这变量。但还是表象,因为<strong>2</strong>其实是赋值给了 <code>yield 1</code> 这个语句,只是因为 <code>x = yield 1</code> 从而把这个2 又再赋值给了x。也就是说,next()的参数会被赋值给 <strong>yield语句</strong>。</p><blockquote><p>因此同理,如果我们的yield语句是 <code>(yield (2 + 3))</code>, 在我们调用 <code>g.next(9)</code> 之后,效果相当于<code>(yield ( 2 + 3 )) === 9</code>。实际上,这个赋值语句是很有必要的,如果没有这里面的 g.next(2), 我们的<strong>x</strong>的值就会是 <code>undefined</code>, yield语句的执行本身并没有值。</p></blockquote><h3 id="4-第三个next:-g-next-3"><a href="#4-第三个next:-g-next-3" class="headerlink" title="4. 第三个next: g.next(3)"></a>4. 第三个next: <code>g.next(3)</code></h3><p> 同第二个类似,next()执行了下一个yield语句,并立即停止执行了。它得到了该yield语句的返回值5,并停下来。这里面的<code>x</code>赋值已经完成了,就是上面设置过的2。</p><h3 id="5-第四个next:-g-next-5"><a href="#5-第四个next:-g-next-5" class="headerlink" title="5. 第四个next: g.next(5)"></a>5. 第四个next: <code>g.next(5)</code></h3><p> 执行了第三个yield语句 <code>yield y</code>, 并获得了y的值,即前面被赋值了的 x(2) + y (3), 结果等于5。由于这是最后一个yield语句,他返回的 {done: true}, 返回结果是最后执行的。</p><h2 id="说了这么多next-和yield语句的细节是要干嘛"><a href="#说了这么多next-和yield语句的细节是要干嘛" class="headerlink" title="说了这么多next()和yield语句的细节是要干嘛"></a>说了这么多next()和yield语句的细节是要干嘛</h2><p>其实是我自己在看到他们的时候犯怵。看到他们传入了参数,但是参数是在哪一步被传入的?yield语句执行的效果到底什么?其实困惑着我,让我没怎么好意思去问和思考进一步的问题,比如使用场景,现在我们可以肯定地说出如下:</p><ol><li><p>next()的参数怎么传值的?<br>其实理解为传入有点不太贴切,传入还是我们现有的思维,仿佛函数的参数传入那么理解就有点跟不上了(当然next传值也是函数的参数=_=)。我觉得把这个 next()的参宿理解为<em>赋值</em>更贴切,为上一个yield语句赋值。那么为什么不在上一个yield语句执行的同时就赋值呢?这就是他令人困惑的地方啊。。</p></li><li><p>yield语句是什么?<br>yield其实就是一个新的JavaScript关键字,他的作用是执行后面的表达式。他的这种表达式执行我们其实见过一个类似的,那就是<code>typeof</code>,typeof也是这么执行的嘛!而且也是返回执行的结果,不过typeof结果返回结果是直接在当前整个表达式中的,而yield语句的返回结果是给<code>next()</code>的,而且他自身在表达式中返回 <code>undefined</code>, 除非我们我们在调用<code>next()</code>的时候传入了值。</p></li></ol>]]></content>
<summary type="html">
<p>其实是在去年的JsConf上才第一次看到有人介绍ES5的generator,以及他的实现语法 <code>yield</code>。当时演讲嘉宾举了一个例子,通过<code>next()</code>去调用,获取返回值,并传入参数,我看得略云里雾里,并且基于我的云里雾里,一
</summary>
<category term="javascript ES5" scheme="http://codingfishman.github.io/tags/javascript-ES5/"/>
</entry>
<entry>
<title>函数声明与函数表达式</title>
<link href="http://codingfishman.github.io/2016/05/29/%E5%87%BD%E6%95%B0%E5%A3%B0%E6%98%8E%E4%B8%8E%E5%87%BD%E6%95%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F/"/>
<id>http://codingfishman.github.io/2016/05/29/函数声明与函数表达式/</id>
<published>2016-05-29T06:12:49.000Z</published>
<updated>2016-05-30T09:47:56.000Z</updated>
<content type="html"><![CDATA[<p>函数定义几乎是我们做的最多的一件事了,有时候我们以 <code>function a(){}</code> 来申明,有时候以 <code>var a = function(){}</code> 来申明,他们是不是一样呢?<br>比如我在写代码的过程中,经常写下如下代码,但其实就在刚刚,我才似乎终于懂得了一些下面的函数定义到底发生了什么(附注释):</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ <span class="comment">// 函数表达式</span></span><br><span class="line"><span class="meta"> 'use strict'</span></span><br><span class="line"> angular.module(<span class="string">'moduleA'</span>)</span><br><span class="line"> .controller(<span class="string">'controllerA'</span>, ControllerA)</span><br><span class="line"></span><br><span class="line"> ControllerA.$inject = []</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">ControllerA</span> (<span class="params"></span>) </span>{ <span class="comment">// 函数声明,变量提升</span></span><br><span class="line"> <span class="comment">// the actual body</span></span><br><span class="line"> }</span><br><span class="line">})() <span class="comment">// 通过里面的(), 使函数体代码块被当做函数表达式执行,函数名可以忽略</span></span><br></pre></td></tr></table></figure><a id="more"></a><h1 id="函数声明"><a href="#函数声明" class="headerlink" title="函数声明"></a>函数声明</h1><p>简单地说,我们直接在顶部,或函数内部直接定义的函数代码块就是函数声明:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">func</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// func body</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>他有如下约定:</p><ol><li>形如<code>function <funcName> () {}</code>, 里面的四个元素缺一不可</li><li>不属于表达式的一部分</li></ol><blockquote><p>第2点可能有些混淆,什么是<em>表达式的一部分</em>呢?我们以promise的调用为例:</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(...)</span><br><span class="line"></span><br><span class="line">promise.then(<span class="function"><span class="keyword">function</span> <span class="title">success</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">//</span></span><br><span class="line"> }, <span class="function"><span class="keyword">function</span> <span class="title">failed</span> (<span class="params"></span>) </span>{})</span><br></pre></td></tr></table></figure><blockquote><p> 可以看到在第3行和第5行中,我们定义的函数方式<em>与函数声明的要求一致</em>,然而这里的函数定义就是做为<em>表达式的一部分</em>而存在的,因此不是函数定义。这里的表示式是什么概念呢?我们稍后会提到。</p></blockquote><p>函数声明会直接申明一个可调用的函数,其函数名就是申明时的函数名。同时函数声明具有变量提升的效果,故而我们可以在函数声明的前面,直接调用该函数变量。函数声明还是比较单纯的。</p><h1 id="函数表达式"><a href="#函数表达式" class="headerlink" title="函数表达式"></a>函数表达式</h1><blockquote><p>在周爱民老师的《JAVASCRIPT语言精髓与编程实践》一书中,这样定义“表达式”和“程序”:<br>表达式:有运算符和操作符构成,并产生运算结果的语法结构。比如 <code>1+2+3</code> 就是一个表达式,他会返回6。<br>程序:程序是由语句构成的,语句则是由“;(分号,有时候可以省略)”分隔的句子或命令。在表达式后面加上”;”分隔符,则该表达式就是表达式语句。</p></blockquote><p>简单地说,<code>var func = function () {}</code> 就是一个最常见的函数表达式,他与函数声明有如下区别:</p><ol><li>他是整个“赋值表达式”一部分,而不是独立存在的。比如示例中,该function 及后面的语句,整体都是赋值运算符<code>=</code>中的<em>值</em>,完成这整个赋值运算符的整个过程就是”赋值表达式“在发挥作用</li><li>他可以省略函数名,比如创建匿名函数</li></ol><blockquote><p>还记得我们前面Promise例子中的 <code>function success(){}</code> 及 <code>function failed(){}</code> 吗?我们在这里的定义中也为他们加上了函数名 success 和 failed, 但是他们的实际意义其实就是在源码中给我们看的。。从JS效果上来说,他们与匿名函数没有任何区别,其函数名其实会被直接忽略。我们可以通过一个简单的例子求证:</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> func1 = <span class="function"><span class="keyword">function</span> <span class="title">func</span> (<span class="params"></span>) </span>{<span class="built_in">console</span>.info(<span class="string">'Here is the func body'</span>)}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输入: 'Here is the func body'</span></span><br><span class="line">func1()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 异常: Uncaught ReferenceError: func is not defined</span></span><br><span class="line">func()</span><br></pre></td></tr></table></figure><blockquote><p>也就是说,我们虽然在定义时申明了 <em>func</em> 的变量,但是执行后并没有任何效果,其值被忽略,仍然被当做了匿名函数。</p></blockquote><h1 id="有趣的函数调用"><a href="#有趣的函数调用" class="headerlink" title="有趣的函数调用"></a>有趣的函数调用</h1><p>无论是函数声明还是函数表达式,他们产生的函数都是用来被调用的,而且我们有多种调用函数的方式,如下:</p><blockquote><p>下面主要内容来自周爱民老师的《JAVASCRIPT语言精髓与编程实践》一书,此书和常见的JavaScript书籍略有不同,作者以自己的方式讲述了JavaScript语句背后的原理,很值得一读!</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 示例1 函数声明</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">func1</span> (<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">func1()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 示例2 匿名函数,即函数表达式,通过引用来调用</span></span><br><span class="line"><span class="keyword">var</span> func2 = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">func2()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 示例3 匿名函数,但没有引用(1)</span></span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line">}())</span><br><span class="line"></span><br><span class="line"><span class="comment">// 示例4 匿名函数,但没有引用(2)。和文章开始的困惑示例一样</span></span><br><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line">})()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 示例5 匿名函数,但没有引用(3)</span></span><br><span class="line"><span class="keyword">void</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line">}()</span><br></pre></td></tr></table></figure><p>示例1和示例2都是我们最常用的函数定义和调用方式,就不再细叙。我就按照书本里的内容直白地解释一下示例3、4、5。</p><blockquote><p>在这之前,我们需要知道: <code>()</code>当不用做函数调用时,也具有强制语句执行的效果,也就是可以把它包裹的内容当做表达式来执行。他也是一种运算符,书里称之为“优先级运算符”。</p></blockquote><h4 id="示例3"><a href="#示例3" class="headerlink" title="示例3"></a><code>示例3</code></h4><p>通过最外面的 <code>()</code> 强制执行了一次<em>函数调用运算</em>,该运算同时也执行了匿名函数(函数表达式)的定义,返回函数引用,故而后面的函数调用得以成功。</p><h4 id="示例4"><a href="#示例4" class="headerlink" title="示例4"></a><code>示例4</code></h4><p>首先借助 <code>()</code> 使得匿名函数(函数表达式)执行,由于该表达式是括号内唯一的也是最后的一个表达式,因此其值得以返回<em>(即匿名函数的引用)</em>, 然后后面的 <code>()</code> 作为函数调用的语法,通过引用调用了该匿名函数。</p><h4 id="示例5"><a href="#示例5" class="headerlink" title="示例5"></a><code>示例5</code></h4><blockquote><p>void 是JavaScript中特殊的一个<em>运算符</em>,他使得运算的对象返回undefined。例如 <code>void 1;</code> 也是合法的语句。<strong>作为运算符,他会使运算对象参与运算</strong>。</p></blockquote><p>运算符需要针对<em>值</em>进行运算,因此JS引擎会把后面的语句当做表达式执行以获取值。将其执行的结果作为 void 的参数,void运算结束后,会返回 undefined。因此从某种角度来说,示例5和示例3类似,只是他们触发表达式执行的关键字不一样罢了。</p><p>在示例3、4、5我们总是不停地重复一个名词:<em>表达式得以执行</em>。 其实也就是使得里面的函数定义被当做”函数表达式“,并且得以执行,然后返回其引用。</p><blockquote><p>为什么特别提到”使得里面的函数定义被当做函数表达式“呢?我们知道”函数表达式“和”函数声明“之间的区别,尤其是在都具备了函数名的时候,其实是取决于其是不是属于整个表达式的一部分,故而,如下的代码会不会正常调用呢?</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">func</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'yes, the func again: '</span> + name)</span><br><span class="line">}(<span class="string">'the name'</span>)</span><br></pre></td></tr></table></figure><blockquote><p>答案是否定的,由于没有 <code>()</code> 或 <code>void</code> 的运算符,代码中的 function 定义部分,会被解析成函数声明,而不会返回引用;实际上解析后该代码块如下,函数声明和小括号后面都被加上了<code>;</code>:</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></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">func</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'yes, the func again: '</span> + name)</span><br><span class="line">};</span><br><span class="line"><span class="comment">// 执行一次”优先级运算“并返回 'the name'</span></span><br><span class="line">(<span class="string">'the name'</span>);</span><br></pre></td></tr></table></figure><blockquote><p>如果在前面加上 <code>void</code> 关键字,那么就是我们期待的结果了,由于此时函数定义是表达式中的一部分,故没有被解析成函数声明。</p></blockquote><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p><strong>函数声明</strong>是一个很纯粹的函数声明方法,其函数名就是申明中的函数名,而且其申明必须是干干净净的。同时其申明具有变量提升的效果。</p><p><strong>函数表达式</strong>, 比如匿名函数,或者任何作为表达式一部分的<em>函数定义</em>(此处特意与”函数声明“区别开来)<br>,都是函数表达式(好吧,其实是我目前自己这么理解的,还不能说得这么绝对==)。其被调用的方式,就是通过调用函数表达式的执行所返回的引用。</p><p>如果函数表达式中的函数设置了函数名,如 <code>void function func (){}</code>。里面的函数名 <code>func</code> , 其实只在源码中存在意义,编译后它没有意义,也不可以被引用到,即我们无法通过 <code>func()</code> 来调用该函数。</p><p>同时参考:<a href="http://www.cnblogs.com/TomXu/archive/2011/12/29/2290308.html" target="_blank" rel="external">深入理解JavaScript系列(2):揭秘命名函数表达式</a></p>]]></content>
<summary type="html">
<p>函数定义几乎是我们做的最多的一件事了,有时候我们以 <code>function a(){}</code> 来申明,有时候以 <code>var a = function(){}</code> 来申明,他们是不是一样呢?<br>比如我在写代码的过程中,经常写下如下代码,但其实就在刚刚,我才似乎终于懂得了一些下面的函数定义到底发生了什么(附注释):</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="comment">// 函数表达式</span></span><br><span class="line"><span class="meta"> 'use strict'</span></span><br><span class="line"> angular.module(<span class="string">'moduleA'</span>)</span><br><span class="line"> .controller(<span class="string">'controllerA'</span>, ControllerA)</span><br><span class="line"></span><br><span class="line"> ControllerA.$inject = []</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">ControllerA</span> (<span class="params"></span>) </span>&#123; <span class="comment">// 函数声明,变量提升</span></span><br><span class="line"> <span class="comment">// the actual body</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;)() <span class="comment">// 通过里面的(), 使函数体代码块被当做函数表达式执行,函数名可以忽略</span></span><br></pre></td></tr></table></figure>
</summary>
<category term="JavaScript" scheme="http://codingfishman.github.io/tags/JavaScript/"/>
</entry>
<entry>
<title>flex布局当overflow时无法滑动到顶部</title>
<link href="http://codingfishman.github.io/2016/05/21/flex%E5%B8%83%E5%B1%80%E5%BD%93overflow%E6%97%B6%E6%97%A0%E6%B3%95%E6%BB%91%E5%8A%A8%E5%88%B0%E9%A1%B6%E9%83%A8/"/>
<id>http://codingfishman.github.io/2016/05/21/flex布局当overflow时无法滑动到顶部/</id>
<published>2016-05-21T07:39:38.000Z</published>
<updated>2016-05-21T14:22:16.000Z</updated>
<content type="html"><![CDATA[<p>有了flex布局后,让一个元素居中变得比以前容易了很多,当我们需要全屏展现一个图片时,我们也可以用flex布局使其居中。然而,当这个图片的高度超过外层DIV的高度(宽度占满外层DIV的宽度,高度等比压缩后),我们发现居中后无法滑动到图片的顶部,但是却可以滑动到图片的底部。</p><p>我在JsFiddle上做了一个简单的示例,如下:</p><script async src="https://jsfiddle.net/nickday/b8x75hso/1/embed/html,css,result/"></script><p>可以看到,<strong>flex-center</strong>布局的图片,初始是居中的,但是其顶部部分却再也滑动不到了;而正常的div布局,初始位置从顶部开始,可以一直滑动到顶部。那么这是flex布局的一个Bug么?</p><h1 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h1><p>第一次遇到的时候,我尝试修改<code>align-items: flex-start</code>, 确实如此图片就和普通布局类似,从顶部开始,也可以滑动到底部,然而这会使我们丢失居中的效果。困惑中我Google了一把,遇到了一个特别详细的<a href="http://stackoverflow.com/a/33455342/6266821" target="_blank" rel="external">StackOverflow</a>答案,里面也摘录了<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes#Flex_item_considerations" target="_blank" rel="external">MDN</a>上关于flex布局在scroll的局限性:</p><blockquote><p>Flexbox的对齐是一种“真正”意义上的居中, 这一点和我们使用的其他的CSS居中技术不太一样。也就是说,即便当子元素超过父元素的边界时,他们在父元素中仍然是居中存在的。然而这有时候会带来问题,当他们的top超过父元素的top边界,或者其left超过父元素的left边界时<em>(当高度限定,宽度自动延伸时,left就有可能超过父元素的left边界)</em>,其超过top的部分(或left部分),虽然确实有内容在哪里,实际上却是无法scroll过去的。在将来的版本中,flex的对齐属性会加入一个“安全”的选项(使得在这种情境下,top部分不至于被吞没)。目前,如果这对您是一个担忧的话,您可以通过子元素的<code>auto margins</code>来达到这种安全的布局。当子元素超过父元素时,他们会使用<code>margin</code>属性,而忽略居中属性。不同于 justify- 的系列属性,通过在flex父容器中的第一个和最后一个元素上面设置<code>margin:auto</code><em>(如果整个容器内只有一个元素,比如本例,则直接设置在该子元素上面)</em>,当有足够的空间给子元素来进行”flex”布局时,flex的布局会生效;否则就会切换到一般的布局方式,也就是 <code>margin:auto</code>。</p></blockquote><p>原谅我拙劣直白的翻译和自补充…是不是有点拗口?可以看看链接的<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes#Flex_item_considerations" target="_blank" rel="external">原文</a>更清楚哦。</p><p>结合我们的当前场景来说,也就是当我们的元素没有超过父元素的高度(此处我们是宽度固定,因此只有高度可能超过父元素),图片会被居中展示;但是当子元素超过了父元素的高度时,如果我们针对子元素设置了<code>margin:auto;</code>, 那么flex布局会采用我们的<em>margin</em>属性来布局, 顶部就可以滑动到,由此解决方案就是在子元素<code>img</code>中,加入<code>margin: auto</code>, 如下:</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">.flex-center {</span><br><span class="line"> display: -webkit-flex;</span><br><span class="line"> display: flex;</span><br><span class="line"> -webkit-justify-content: center;</span><br><span class="line"> justify-content: center;</span><br><span class="line"> -webkit-align-items: center;</span><br><span class="line"> align-items: center;</span><br><span class="line"> overflow-y: auto;</span><br><span class="line"> width: 250px;</span><br><span class="line"> height: 200px;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">.flex-center img {</span><br><span class="line"> display: block;</span><br><span class="line"> width: 100%;</span><br><span class="line"> margin: auto; // 设置margin属性</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>JsFiddle上的效果如下:</p><script async src="https://jsfiddle.net/nickday/b8x75hso/3/embed/html,css,result/dark/"></script><p>可以看到,如果遇到了图片高度超过父元素高度,其居中对齐的特性会被抹去,而是从顶部开始布局;当图片高度未超过父元素时,遵循正常的flex-center居中布局。至此我们的目的达成!</p>]]></content>
<summary type="html">
<p>有了flex布局后,让一个元素居中变得比以前容易了很多,当我们需要全屏展现一个图片时,我们也可以用flex布局使其居中。然而,当这个图片的高度超过外层DIV的高度(宽度占满外层DIV的宽度,高度等比压缩后),我们发现居中后无法滑动到图片的顶部,但是却可以滑动到图片的底部。<
</summary>
<category term="CSS" scheme="http://codingfishman.github.io/tags/CSS/"/>
</entry>
<entry>
<title>我们经常看到的"!!obj"</title>
<link href="http://codingfishman.github.io/2016/05/21/%E6%88%91%E4%BB%AC%E7%BB%8F%E5%B8%B8%E7%9C%8B%E5%88%B0%E7%9A%84%22!!obj%22/"/>
<id>http://codingfishman.github.io/2016/05/21/我们经常看到的"!!obj"/</id>
<published>2016-05-21T05:56:15.000Z</published>
<updated>2016-05-28T05:29:48.000Z</updated>
<content type="html"><![CDATA[<p>相信大家在偶尔看到一些库的源码时,总是能看到这样一句语法<code>a === true && !!obj;</code>, 是的,这里面用了<strong>!!obj</strong> 这样一个运算符。我们不禁好奇,这样做的结果不就是等于什么都没做吗?最近在看老姚的<a href="http://www.w3cfuns.com/notes/17398/1762c504ef3b9487a1f2873e8b89c188.html" target="_blank" rel="external">《underscore源码解析》</a>恰好也有这么一段,作者做了解释,我自己也求证了一下。</p><h1 id="isObject-方法"><a href="#isObject-方法" class="headerlink" title="_.isObject()方法"></a>_.isObject()方法</h1><p>源码如下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">_.isObject = <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> type = <span class="keyword">typeof</span> obj;</span><br><span class="line"> <span class="keyword">return</span> type === <span class="string">'function'</span> || type === <span class="string">'object'</span> && !!obj;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>可以看到,如果 <code>typeof</code> 的值为<em>‘function’</em>则直接返回<code>true</code>, 否则返回<em>是否是object</em>。 但是其实多做了一步,作者解释是为了避免null的值。</p><blockquote><p>后面的!!obj是拿到obj的布尔值。目的是为了排除null。</p></blockquote><h1 id="obj-返回了什么"><a href="#obj-返回了什么" class="headerlink" title="!!obj 返回了什么"></a>!!obj 返回了什么</h1><p>我们申明简单的对象,以及空对象,可以直接看到这段表达式返回了什么</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正常对象</span></span><br><span class="line"><span class="keyword">var</span> normalObj = {}</span><br><span class="line"><span class="built_in">console</span>.log(!normalObj) <span class="comment">// false</span></span><br><span class="line"><span class="built_in">console</span>.log(!!normalObj) <span class="comment">// ture</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// null</span></span><br><span class="line"><span class="built_in">console</span>.log(!<span class="literal">null</span>) <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(!!<span class="literal">null</span>) <span class="comment">// false</span></span><br></pre></td></tr></table></figure><p>对于正常的非空对象,返回<code>true</code>,对于null则返回<code>false</code>。But wait!!, 这有啥用?难道我们平时不是直接 <code>if(type === 'object' && obj) {}</code> 就可以了吗?</p><h1 id="obj-为什么要这么写"><a href="#obj-为什么要这么写" class="headerlink" title="!!obj 为什么要这么写"></a>!!obj 为什么要这么写</h1><p>其实作者有举一个例子说明:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="number">20</span> && <span class="number">30</span>) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'The result is ture'</span>) <span class="comment">// 会执行</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>此处 if语句的返回结果显然是true, 但是 <code>20 && 30</code> 的返回结果其实是<em>30</em>(因为会读取最后一个的值,故而 <code>20 && 30 && 10</code>会返回<em>10</em>。同理,<code>20 || 30 || 10</code>会返回 <em>20</em>), 之所以返回了true是因为<code>if</code>语句进行的类型转换。</p><p>对于不在if语句以内的表达式,如果我们也需要返回bool值呢?是的这时候<code>!!obj</code>就派上了用场,他返回的是bool值,而且恰好和我们需求中的<em>是否为null</em>对应了起来。</p><p>因此 <code>!!</code> 并没有什么特殊的功能,他就是做了两次<strong>非</strong>运算,然后返回了运算的结果, <code>true</code> 或者 <code>false</code>。</p>]]></content>
<summary type="html">
<p>相信大家在偶尔看到一些库的源码时,总是能看到这样一句语法<code>a === true &amp;&amp; !!obj;</code>, 是的,这里面用了<strong>!!obj</strong> 这样一个运算符。我们不禁好奇,这样做的结果不就是等于什么都没做吗?最近
</summary>
<category term="Javascript" scheme="http://codingfishman.github.io/tags/Javascript/"/>
</entry>
<entry>
<title>Prerender预渲染优化SEO</title>
<link href="http://codingfishman.github.io/2016/05/06/prerender%E9%A2%84%E6%B8%B2%E6%9F%93%E4%BC%98%E5%8C%96SEO/"/>
<id>http://codingfishman.github.io/2016/05/06/prerender预渲染优化SEO/</id>
<published>2016-05-06T14:55:15.000Z</published>
<updated>2016-05-20T04:03:47.000Z</updated>
<content type="html"><![CDATA[<p>单页面应用的主要内容都依赖于JS的执行,当其首页面下载下来的时候,其实不是完整的页面,而是HTML + JS文件,浏览器提供执行环境于是页面被渲染了出来。用户在访问的时候体验会很好,但是对于搜索引擎的爬虫就不太友善了,因为他们不能执行JS,这时候<a href="https://prerender.io/documentation" target="_blank" rel="external">Prerender</a>就派上用场了,它可以帮忙把页面渲染完成之后再返回给爬虫工具,我们的页面也就能被解析到了。最近我尝试搭建了基于本地的Prerender + NGINX的预渲染服务,希望可以帮到大家。</p><h2 id="前置条件"><a href="#前置条件" class="headerlink" title="前置条件"></a>前置条件</h2><p>由于本地的Prerender服务需要有NodeJs环境支持,如果之前服务器环境没有NodeJs需要先进行安装,可参考<a href="https://nodejs.org/en/" target="_blank" rel="external">官网</a>。</p><h2 id="安装Prerender"><a href="#安装Prerender" class="headerlink" title="安装Prerender"></a>安装Prerender</h2><p>此处我们假定安装目录在 <em>/opt/</em> 下</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /opt</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/prerender/prerender.git</span><br><span class="line"><span class="built_in">cd</span> prerender</span><br><span class="line"></span><br><span class="line"><span class="comment"># Phantomjs 官方的下载地址会超时,此处重新指定其下载地址为淘宝镜像</span></span><br><span class="line"><span class="built_in">export</span> PHANTOMJS_CDNURL=https://npm.taobao.org/mirrors/phantomjs</span><br><span class="line">npm install</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动Server.js, 默认监听3000端口</span></span><br><span class="line">node server.js</span><br></pre></td></tr></table></figure><p>可以通过<em>curl</em>进行测试,看看返回的内容是不是解析后的内容</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果按照成功,通过3000端口访问后的页面内容是已经解析过的</span></span><br><span class="line">curl http://localhost:3000/http://hostname.com/webpath</span><br></pre></td></tr></table></figure><h2 id="配置NGINX"><a href="#配置NGINX" class="headerlink" title="配置NGINX"></a>配置NGINX</h2><p>我们假设项目的访问路径为 <a href="http://hostname.com/webpath/anything" target="_blank" rel="external">http://hostname.com/webpath/anything</a>, 将所有/webpath/*的请求当做项目的入口地址。参考<a href="https://gist.github.com/thoop/8165802" target="_blank" rel="external">官方配置</a></p><blockquote><p>我们只需要针对搜索引擎的访问进行Prerender预渲染,正常的浏览器访问我们避免通过Prerender进行渲染</p></blockquote><figure class="highlight nginx"><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></pre></td><td class="code"><pre><span class="line"><span class="attribute">location</span><span class="regexp"> ^~</span> /webpath/ {</span><br><span class="line"> <span class="attribute">set</span> <span class="variable">$prerender</span> <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">if</span> (<span class="variable">$http_user_agent</span> <span class="regexp">~* "baiduspider|twitterbot|facebookexternalhit|rogerbot|embedly|quora</span> link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator<span class="string">") {</span><br><span class="line"> set <span class="variable">$prerender</span> 1;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> # 谷歌推荐的配置方式</span><br><span class="line"> if (<span class="variable">$args</span> ~ "</span>_escaped_fragment_<span class="string">") {</span><br><span class="line"> set <span class="variable">$prerender</span> 1;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (<span class="variable">$prerender</span> = 1) {</span><br><span class="line"> # 官方推荐通过<span class="variable">$prerender</span>变量来进行跳转来解决NGINX缓存</span><br><span class="line"> set <span class="variable">$prerender</span> "</span><span class="number">127.0.0.1:3000</span><span class="string">";</span><br><span class="line"></span><br><span class="line"> rewrite .* /<span class="variable">$scheme</span>://<span class="variable">$host</span><span class="variable">$request_uri</span>? break;</span><br><span class="line"> proxy_pass http://<span class="variable">$prerender</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> # 此处,我们假设主页面html为 /index.html</span><br><span class="line"> try_files <span class="variable">$uri</span><span class="variable">$args</span> /index.html;</span><br><span class="line">}</span></span><br></pre></td></tr></table></figure><p>在上面的NGINX配置中,我们针对请求参数包括 <em>_escaped_fragment_</em> 也启用了Prerender服务,官方说明表示谷歌会通过这种方式来抓取单页面应用。而我们也可以借助这个特点很方便地测试配置是否正确:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 不再需要直接访问 localhost:3000的地址了</span></span><br><span class="line">curl http://hostname.com/webpath/anything?_escaped_fragment_=</span><br></pre></td></tr></table></figure><p>在配置NGINX以前,以上返回的只是<em>index.html</em>的内容, 如果配置成功则会返回解析后的内容。</p><h2 id="将Prerender加入守护进程"><a href="#将Prerender加入守护进程" class="headerlink" title="将Prerender加入守护进程"></a>将Prerender加入守护进程</h2><p>上面的功能虽然能运行起来了,但是Prerender服务是依赖于SSH终端的,如果这个终端被关闭,那么服务也就被关闭了,为此我们需要将Prerender加入守护进程。<br>我直接使用了Git上的一个<a href="https://gist.github.com/renebakx/c18b5aab21b06150e12e" target="_blank" rel="external">Prerender守护进程模板</a>,并将其配置成我的环境,如下:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span><br><span class="line"></span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># chkconfig: 35 99 99</span></span><br><span class="line"><span class="comment"># description: prerender.js server for the given user</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"></span><br><span class="line">. /etc/rc.d/init.d/<span class="built_in">functions</span></span><br><span class="line"></span><br><span class="line">APPNAME=<span class="string">"Prerender"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 用来运行Prerender服务的用户</span></span><br><span class="line">USER=<span class="string">"<user that runs prerender>"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Node命令的路径,此处我直接配置为"node",因为node已经加入全局变量</span></span><br><span class="line">DAEMON=<span class="string">"node"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Prerender的安装目录,即是我们拷贝下来的Prerender文件夹目录,此处为 /opt/prerender</span></span><br><span class="line">ROOT_DIR=<span class="string">"<path to prerender>"</span></span><br><span class="line"></span><br><span class="line">SCRIPT=<span class="string">"<span class="variable">$(basename $0)</span>"</span></span><br><span class="line">PIDFILE=<span class="string">"/var/run/<span class="variable">$SCRIPT</span>.pid"</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">SERVER=<span class="string">"<span class="variable">$ROOT_DIR</span>/server.js"</span></span><br><span class="line">LOG_FILE=<span class="string">"/var/log/prerender.log"</span></span><br><span class="line">SHUTDOWN_WAIT=20</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">app_pid</span></span>()</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">echo</span> `ps -aefw | grep <span class="string">"<span class="variable">$DAEMON</span> <span class="variable">$SERVER</span>"</span> | grep -v <span class="string">" grep "</span> | awk <span class="string">'{print $2}'</span>`</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">do_start</span></span>()</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">echo</span> -n $<span class="string">"Starting <span class="variable">$SERVER</span>: "</span></span><br><span class="line"> pid=$(app_pid)</span><br><span class="line"> <span class="keyword">if</span> [ -n <span class="string">"<span class="variable">$pid</span>"</span> ]</span><br><span class="line"> <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"<span class="variable">$APPNAME</span> is already running (pid: <span class="variable">$pid</span>)"</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="keyword">if</span> [ ! -w <span class="variable">$LOG_FILE</span> ]</span><br><span class="line"> <span class="keyword">then</span></span><br><span class="line"> <span class="keyword">if</span> [ ! -e <span class="variable">$LOG_FILE</span> ]</span><br><span class="line"> <span class="keyword">then</span></span><br><span class="line"> touch <span class="variable">$LOG_FILE</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> chown <span class="variable">$USER</span> <span class="variable">$LOG_FILE</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"<span class="variable">$APPNAME</span> is not running - starting it up!"</span></span><br><span class="line"> runuser --shell=/bin/bash -l <span class="string">"<span class="variable">$USER</span>"</span> -c <span class="string">"<span class="variable">$DAEMON</span> <span class="variable">$SERVER</span> >> <span class="variable">$LOG_FILE</span> 2>&1 &"</span></span><br><span class="line"> pid=$(app_pid)</span><br><span class="line"> <span class="built_in">echo</span> <span class="variable">$pid</span> > <span class="variable">${PIDFILE}</span></span><br><span class="line"> do_status</span><br><span class="line"> RETVAL=$?</span><br><span class="line"> <span class="built_in">echo</span></span><br><span class="line"> [ <span class="variable">$RETVAL</span> -eq 0 ]</span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">return</span> 0</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">do_wait</span></span>()</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"Waiting for process to exit"</span>;</span><br><span class="line"> pid=$(app_pid)</span><br><span class="line"> <span class="keyword">if</span> [ -n <span class="string">"<span class="variable">$pid</span>"</span> ]</span><br><span class="line"> <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">let</span> kwait=<span class="variable">$SHUTDOWN_WAIT</span></span><br><span class="line"> count=0;</span><br><span class="line"> until [ `ps -p <span class="variable">$pid</span> | grep -c <span class="variable">$pid</span>` = <span class="string">'0'</span> ] || [ <span class="variable">$count</span> -gt <span class="variable">$kwait</span> ]</span><br><span class="line"> <span class="keyword">do</span></span><br><span class="line"> <span class="built_in">echo</span> -n -e <span class="string">"\nwaiting for processes to exit"</span>;</span><br><span class="line"> sleep 1</span><br><span class="line"> <span class="built_in">let</span> count=<span class="variable">$count</span>+1;</span><br><span class="line"> <span class="keyword">done</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">return</span> 0</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">do_stop</span></span>()</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">echo</span> -n $<span class="string">"Stopping <span class="variable">$APPNAME</span> : "</span></span><br><span class="line"> pid=`ps -aefw | grep <span class="string">"<span class="variable">$DAEMON</span> <span class="variable">$SERVER</span>"</span> | grep -v <span class="string">" grep "</span> | awk <span class="string">'{print $2}'</span>`</span><br><span class="line"> <span class="built_in">kill</span> -9 <span class="variable">$pid</span> > /dev/null 2>&1 && echo_success || echo_failure</span><br><span class="line"> <span class="keyword">if</span> [ -f <span class="variable">${PIDFILE}</span> ]; <span class="keyword">then</span></span><br><span class="line"> rm <span class="variable">${PIDFILE}</span></span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> RETVAL=$?</span><br><span class="line"> <span class="built_in">echo</span></span><br><span class="line"> [ <span class="variable">$RETVAL</span> -eq 0 ]</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">do_status</span></span>()</span><br><span class="line">{</span><br><span class="line"> pid=$(app_pid)</span><br><span class="line"> <span class="keyword">if</span> [ -n <span class="string">"<span class="variable">$pid</span>"</span> ]</span><br><span class="line"> <span class="keyword">then</span></span><br><span class="line"> <span class="built_in">echo</span> -n <span class="string">"<span class="variable">$APPNAME</span> is running (pid: <span class="variable">$pid</span>)"</span></span><br><span class="line"> echo_success</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> <span class="built_in">echo</span> -n <span class="string">"<span class="variable">$APPNAME</span> is not running"</span></span><br><span class="line"> echo_failure</span><br><span class="line"> <span class="keyword">fi</span></span><br><span class="line"> <span class="built_in">echo</span></span><br><span class="line"> <span class="built_in">return</span> 0</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">case</span> <span class="string">"<span class="variable">$1</span>"</span> <span class="keyword">in</span></span><br><span class="line"> start)</span><br><span class="line"> do_start</span><br><span class="line"> ;;</span><br><span class="line"> stop)</span><br><span class="line"> do_stop</span><br><span class="line"> ;;</span><br><span class="line"> status)</span><br><span class="line"> do_status</span><br><span class="line"> ;;</span><br><span class="line"> restart)</span><br><span class="line"> do_stop</span><br><span class="line"> do_wait</span><br><span class="line"> do_start</span><br><span class="line"> ;;</span><br><span class="line"> *)</span><br><span class="line"> <span class="built_in">echo</span> <span class="string">"Usage: <span class="variable">$0</span> {start|stop|restart|status}"</span></span><br><span class="line"> RETVAL=1</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">exit</span> <span class="variable">$RETVAL</span></span><br></pre></td></tr></table></figure><p>将此文件命名为 <em>prerender</em>, 并拷贝至 <code>/etc/init.d/</code> 目录下。然后我们重新载入脚本:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 服务新增文件可执行权限</span></span><br><span class="line">chmod +x /etc/init.d/prerender</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重新载入systemctl</span></span><br><span class="line">systemctl daemon-reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动</span></span><br><span class="line">service prerender start</span><br></pre></td></tr></table></figure><p>后续,我们可以通过 <code>service prerender start|stop|restart</code> 来起停服务,同时Prerender也会随机启动。</p>]]></content>
<summary type="html">
<p>单页面应用的主要内容都依赖于JS的执行,当其首页面下载下来的时候,其实不是完整的页面,而是HTML + JS文件,浏览器提供执行环境于是页面被渲染了出来。用户在访问的时候体验会很好,但是对于搜索引擎的爬虫就不太友善了,因为他们不能执行JS,这时候<a href="https
</summary>
<category term="Angular Prerender SEO NGINX" scheme="http://codingfishman.github.io/tags/Angular-Prerender-SEO-NGINX/"/>
</entry>
<entry>
<title>JS本地化的尝试</title>
<link href="http://codingfishman.github.io/2016/04/30/JS%E6%9C%AC%E5%9C%B0%E5%8C%96%E7%9A%84%E5%B0%9D%E8%AF%95/"/>
<id>http://codingfishman.github.io/2016/04/30/JS本地化的尝试/</id>
<published>2016-04-30T08:28:16.000Z</published>
<updated>2017-04-09T14:53:05.000Z</updated>
<content type="html"><![CDATA[<p>还记得去年在百姓网组织的一次meetup上,听到过sofish分享了饿了么前端的一些架构,当时印象最深的是proxy_pass解决跨域问题,只听过词汇却不知道是什么意思。后来大量依赖NGINX,proxy_pass已经可以说是用得最多的一个配置了。还记得当时他们提到了请求合并、JS本地化,觉得实为厉害,但一直都没有机会实践。前不久突然想到我们的前端项目,尤其是通过APP加载的H5工程,应该很适合做本地化,于是也实践了一下。</p><h2 id="基本原理"><a href="#基本原理" class="headerlink" title="基本原理"></a>基本原理</h2><p>通过将JS首次访问时缓存在localstorage中,后续的再次访问都从localstorage中读取js文件,也就不用再下载这个JS了(一般也是整个工程中最大的文件)。同时,当我们服务器端的js有改动更新时,我们也需要更新该缓存中的JS文件。</p><p>由于每次文件有变动时,文件名都会改变,我想找到一个支持通过token来更新本地JS的库,这样和目前的项目结构最匹配,我发现<a href="https://addyosmani.com/basket.js/" target="_blank" rel="external">basket.js</a> 恰好满足这个要求。</p><blockquote><p>basket.js 不能单独使用,它依赖于<a href="https://github.com/tildeio/rsvp.js" target="_blank" rel="external">rsvp.js</a>来异步加载JS,其本身只是localstorage的操作逻辑。</p></blockquote><h2 id="改造"><a href="#改造" class="headerlink" title="改造"></a>改造</h2><p>我直接在首页文件中加入改JS代码,而没有再创建其他文件来做这件事。 因为只需要在生产环境做本地化缓存,默认将这段代码注释。<br>同时,项目的JS文件是动态生成的,其文件名也会跟随着JS内容变化而变化,故文件名是需要在编译器做静态替换的。</p><blockquote><p>此处没有注释,为了markdown语法高亮的效果</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* COMMENT_BEGIN_PLACEHOLDER */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">loadJs</span> (<span class="params">path</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> mobileScript = <span class="built_in">document</span>.createElement(<span class="string">'script'</span>)</span><br><span class="line"> mobileScript.setAttribute(<span class="string">'src'</span>, path)</span><br><span class="line"> <span class="built_in">document</span>.body.appendChild(mobileScript)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">basketload</span> (<span class="params"></span>) </span>{</span><br><span class="line"> basket.require(</span><br><span class="line"> {<span class="attr">url</span>:<span class="string">'MAIN-JS-PLACEHOLDER'</span>, <span class="attr">key</span>: <span class="string">'main-js'</span>, <span class="attr">unique</span>:<span class="string">'MAIN_JS_UNIQUE_HOLDER'</span>, <span class="attr">expire</span>: <span class="number">168</span>}</span><br><span class="line"> )</span><br><span class="line"> .then(<span class="function"><span class="keyword">function</span> (<span class="params">success</span>) </span>{</span><br><span class="line"></span><br><span class="line"> }, <span class="function"><span class="keyword">function</span> (<span class="params">error</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.error(error)</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">window</span>.localStorage) {</span><br><span class="line"> basketload()</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'不支持localStorage'</span>)</span><br><span class="line"> loadJs(<span class="string">'MAIN-JS-PLACEHOLDER'</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">} <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="built_in">console</span>.warn(<span class="string">'尝试读取local storage 失败'</span>)</span><br><span class="line"> loadJs(<span class="string">'MAIN-JS-PLACEHOLDER'</span>)</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* COMMENT_END_PLACEHOLDER */</span></span><br></pre></td></tr></table></figure><blockquote><p>在此,我使用了 <code>MAIN-JS-PLACEHOLDER</code> 来作为生成JS文件名的placeholder, 使用 <code>MAIN_JS_UNIQUE_HOLDER</code>来当做该JS是否变化的token标示符。也可以直接使用文件名做标识符,不过我其实取出了文件名中的MD5作为标识符。同时,我设置了过期时间为168小时,即一星期。</p></blockquote><p>通过编译工具,将上面代码中的placeholder替换完成,并将注释符号删除,就可以使其工作了。 在如上代码中,我也针对一些不支持localstorage的场景,比如隐身模式,做了异常处理,针对这些场景我会直接请求原始JS代码。</p><h2 id="后话"><a href="#后话" class="headerlink" title="后话"></a>后话</h2><p>有的同学可能发现,我上面的代码有一个前提,那就是已经提前加载了 baseket.js (basket.js+rsvp.js可以手工合并)。既然都已经判断是否支持localstorage,那么为何不在判断结束后再插入basket.js呢,这样在不支持localstorage的地方,就可以节省一个不必要的请求呀?</p><p>确实我一开始也这么打算,因为真的很不喜欢请求多余的资源,而且既然这样可以实现何必不呢?<br>因此一开始的版本其实是这样的:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><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="keyword">try</span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">window</span>.localStorage) {</span><br><span class="line"> loadJs(<span class="string">'/pathofjs/basket.min.js'</span>)</span><br><span class="line"> <span class="comment">// 通过onload事件,在JSbasket.min.js加载结束后,加载我们真正需要的JS文件</span></span><br><span class="line"> <span class="built_in">window</span>.onload = basketload</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'不支持localStorage'</span>)</span><br><span class="line"> loadJs(<span class="string">'MAIN-JS-PLACEHOLDER'</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="built_in">console</span>.warn(<span class="string">'尝试读取local storage 失败'</span>)</span><br><span class="line"> loadJs(<span class="string">'MAIN-JS-PLACEHOLDER'</span>)</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>看起来也没什么问题,但是问题还是来了,来自中国移动!由于该网站还没有切换到HTTPS,中国移动在里面劫入了自己的JS文件,域名赤裸裸地绑定在10086.cn下。我们在测试的时候,发现移动页面很久才能打开。经调试,中国移动的一个js文件(base.js, 才几KB),有时候要经过50多秒才能下载完,<code>window.onload</code>也会等待这个下载完才执行,因此页面内容也不会被初始化。项目正在迁移HTTPS,但毕竟还有个过程,因此忍痛放弃了这种方式,直接加载baseket.js了。</p><blockquote><p>basket.js 在我禁用缓存进行测试时,会抛一个读取localstorage的异常, 还是毕竟烦人,故而我在里面加入了一段主动判断localstorage的地方,并catch这个异常,虽然没什么用,但是毕竟没有红色警告了。。</p></blockquote><p>以上,JS本地化的迁移就算是告一段落了。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>标题<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">base</span> <span class="attr">href</span>=<span class="string">"/"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"root"</span> <span class="attr">style</span>=<span class="string">"height: 100%"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>sss</p>]]></content>
<summary type="html">
<p>还记得去年在百姓网组织的一次meetup上,听到过sofish分享了饿了么前端的一些架构,当时印象最深的是proxy_pass解决跨域问题,只听过词汇却不知道是什么意思。后来大量依赖NGINX,proxy_pass已经可以说是用得最多的一个配置了。还记得当时他们提到了请求合
</summary>
<category term="JavaScript 性能优化" scheme="http://codingfishman.github.io/tags/JavaScript-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/"/>
</entry>
<entry>
<title>NGINX add_header 在try_files中不生效</title>
<link href="http://codingfishman.github.io/2016/04/30/NINGX-try-files-And-add-header/"/>
<id>http://codingfishman.github.io/2016/04/30/NINGX-try-files-And-add-header/</id>
<published>2016-04-30T06:55:06.000Z</published>
<updated>2016-05-20T04:02:35.000Z</updated>
<content type="html"><![CDATA[<p>由于项目是单页面应用,整个项目的JS文件其实包含了绝大部分的功能。项目发布时,会对文件名进行MD5重命名,因此以为缓存问题已被解决。但是在实际运行中,却发现总有发生没有请求最新文件的情况。那不就是项目的入口index.html被缓存了呗,得咱配置一个<code>cache-control nocache</code>吧!于是我GET到了一个新知识,因为这里面有误区。</p><h2 id="NGINX基本配置"><a href="#NGINX基本配置" class="headerlink" title="NGINX基本配置"></a>NGINX基本配置</h2><p>所有以 <em>/mobile/</em> 开始的URL请求都会被定向到 <em>mobile.html</em> 文件,由其进行路由映射。</p><figure class="highlight nginx"><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">## server中已经指定root的path如下</span></span><br><span class="line"><span class="comment"># root /path/project/dist</span></span><br><span class="line"><span class="attribute">location</span><span class="regexp"> ^~</span> / {</span><br><span class="line"> <span class="attribute">access_log</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">expires</span> <span class="number">30d</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="attribute">location</span><span class="regexp"> ^~</span> /mobile/ {</span><br><span class="line"> <span class="attribute">try_files</span> <span class="variable">$uri</span><span class="variable">$args</span> /mobile.html;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="配置Cache-Control-解决mobile-html的缓存问题"><a href="#配置Cache-Control-解决mobile-html的缓存问题" class="headerlink" title="配置Cache-Control 解决mobile.html的缓存问题"></a>配置Cache-Control 解决mobile.html的缓存问题</h2><figure class="highlight nginx"><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="attribute">location</span><span class="regexp"> ^~</span> /mobile/ {</span><br><span class="line"> <span class="attribute">add_header</span> Cache-Control <span class="literal">no</span>-cache;</span><br><span class="line"> <span class="attribute">try_files</span> <span class="variable">$uri</span><span class="variable">$args</span> /mobile.html;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然而浏览器查看response-header, <em>Cache-Control:max-age=2592000</em> 仍然在里面,也就是说Cache-Control没生效</p><h2 id="try-file会导致前面配置的-add-header-直接丢失"><a href="#try-file会导致前面配置的-add-header-直接丢失" class="headerlink" title="try_file会导致前面配置的 add_header 直接丢失"></a>try_file会导致前面配置的 add_header 直接丢失</h2><p>我开始怀疑是不是 <em>Cache-Control</em> 与 <em>try_files</em> 不搭?但是没有搜索到相关资料,那么是 <em>add_header</em> 和 <em>try_files</em> 不搭?确实,<em>try_files</em>匹配后,会直接进入匹配的location中的配置,我们在这里配置的都会被丢弃,<a href="http://serverfault.com/questions/538890/nginx-expires-header-not-working" target="_blank" rel="external">ServerFault</a>,也就是说我们要把这一段配置到 <code>/mobile.html</code> 中去:</p><figure class="highlight nginx"><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="attribute">location</span> = /mobile.html {</span><br><span class="line"> <span class="attribute">add_header</span> Cache-Control <span class="literal">no</span>-cache;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="attribute">location</span><span class="regexp"> ^~</span> /mobile/ {</span><br><span class="line"> <span class="attribute">try_files</span> <span class="variable">$uri</span><span class="variable">$args</span> /mobile.html;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样,只会对<em>mobile.html</em>这个文件禁用缓存,其他的静态资源还是会被缓存起来,我们的目的达到了。</p>]]></content>
<summary type="html">
<p>由于项目是单页面应用,整个项目的JS文件其实包含了绝大部分的功能。项目发布时,会对文件名进行MD5重命名,因此以为缓存问题已被解决。但是在实际运行中,却发现总有发生没有请求最新文件的情况。那不就是项目的入口index.html被缓存了呗,得咱配置一个<code>cache-
</summary>
<category term="NGINX" scheme="http://codingfishman.github.io/tags/NGINX/"/>
</entry>
<entry>
<title>基于最基本的float+overflow实现calc的CSS效果</title>
<link href="http://codingfishman.github.io/2016/04/24/%E5%9F%BA%E4%BA%8E%E6%9C%80%E5%9F%BA%E6%9C%AC%E7%9A%84float-overflow%E5%AE%9E%E7%8E%B0%E5%8A%A8%E6%80%81%E5%B8%83%E5%B1%80/"/>
<id>http://codingfishman.github.io/2016/04/24/基于最基本的float-overflow实现动态布局/</id>
<published>2016-04-24T14:31:47.000Z</published>
<updated>2016-04-24T15:29:49.000Z</updated>
<content type="html"><![CDATA[<p>CALC() 是一种正在试验阶段的CSS属性(刚才查了一下,在PC上的支持其实还不错,但是手机端的支持还不好),而且是CSS中比较少的可以在运行时计算的属性值,通过它我们可以实现这种效果:左右布局,左边固定,右边自动铺满。只是限于其支持度我们总是不敢完全放心的使用它。前不久在360的奇舞周刊公众号看看到一篇文章,<a href="https://mp.weixin.qq.com/s?__biz=MzA4NjE3MDg4OQ==&mid=403391330&idx=1&sn=dc2e5c44abc0727dc1d3f64b23f1663a" target="_blank" rel="external">传送门</a>,作者(<em>瓜瓜</em>) 介绍了BFC(块式布局上下文), 并总结了BFC的几条原则,不多,但是有一条可以帮助我们实现很多情况下<em>calc</em>才能办到的效果。</p><p>她总结下来BFC规则包含如下(<a href="https://mp.weixin.qq.com/s?__biz=MzA4NjE3MDg4OQ==&mid=403391330&idx=1&sn=dc2e5c44abc0727dc1d3f64b23f1663a" target="_blank" rel="external">原文</a>更有相关示例,推荐前往阅读)</p><ul><li>内部的Box会在垂直方向,一个接一个地放置。</li><li>Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠</li><li>每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。</li><li>BFC的区域不会与float box重叠。</li><li>BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。</li><li>计算BFC的高度时,浮动元素也参与计算</li></ul><p>之前我们总是使用的<code>overflow:hidden</code>来包裹浮动元素的高度,其实就是利用了BFC的规范</p><blockquote><p>计算BFC的高度时,浮动元素也参与计算</p></blockquote><p>而且里面提到了一条规则可以直接<strong>实现前面场景下的calc效果</strong></p><blockquote><p>BFC的区域不会与float box重叠。</p></blockquote><p>这句话本来没什么,但是这里面的<em>BFC区域不换行</em>,同时他又不会与float区域重叠,这实际上就是我们前面想要的效果:左边固定宽度,右边铺满全屏,所需代码很少,如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.slide</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">120px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">300px</span>;</span><br><span class="line"> <span class="attribute">float</span>: left;</span><br><span class="line"> <span class="attribute">background</span>: <span class="number">#f66</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.main</span> {</span><br><span class="line"> <span class="attribute">background</span>: <span class="number">#fcc</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">500px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"slide"</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"main"</span> ></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>JsFiddler示例:</p><script async src="https://jsfiddle.net/nickday/rowbnzdg/embed/html,css,result/dark/"></script><p>修改浏览器大小,大家会发现,右边的区域会自动伸缩,但是却不会越过左边。而且,他们会一直顶部平行。谢谢奇舞团的这篇介绍:)</p><p>第一次意识这个规则并联想到它可能的用处时,自己着实惊了一下。也瞬间觉得,无论是CSS魔法哥,还是其他前辈大神,他们的厉害并不是莫名其妙的事情,在厉害的背后必然充满了思考,充满了学习。</p>]]></content>
<summary type="html">
<p>CALC() 是一种正在试验阶段的CSS属性(刚才查了一下,在PC上的支持其实还不错,但是手机端的支持还不好),而且是CSS中比较少的可以在运行时计算的属性值,通过它我们可以实现这种效果:左右布局,左边固定,右边自动铺满。只是限于其支持度我们总是不敢完全放心的使用它。前不久
</summary>
<category term="CSS BFC" scheme="http://codingfishman.github.io/tags/CSS-BFC/"/>
</entry>
<entry>
<title>借助background-size实现漂亮的avator头像</title>
<link href="http://codingfishman.github.io/2016/04/16/%E5%80%9F%E5%8A%A9background-position%E5%AE%9E%E7%8E%B0%E7%BE%8E%E8%A7%82%E7%9A%84avator%E5%A4%B4%E5%83%8F/"/>
<id>http://codingfishman.github.io/2016/04/16/借助background-position实现美观的avator头像/</id>
<published>2016-04-16T12:35:26.000Z</published>
<updated>2016-04-18T07:26:57.000Z</updated>
<content type="html"><![CDATA[<p>可以说,但凡涉及到用户信息展示的页面,用户自定义的头像总是让人记得最深刻,我们也总在这上面花去不少心思。从正方形,到圆角,在到现在的全圆,我们的头像展示也经历着自己的演进。之前我在处理个人头像的时候,想到的总是使用一个DIV+IMG,DIV 正方形并使用50%的圆角,盖在图片IMG的上面。但是这样有一个问题,就是当图片的长宽比很大时,我们要么强行拉伸图片使其不安比例的铺满,要么就等比拉伸进而导致图片不能撑满整个圆形DIV,如果不使用JS,这似乎无解? 哈哈,我们忽略了CSS的一个属性:<strong>background-size</strong></p><p>相信我们在图片布局中,总是用到过最新的 <code>background</code> 相关CSS属性,而其中的 <code>background-size</code> 有一个很棒的属性值 <em>cover</em>.</p><h2 id="background-size-cover"><a href="#background-size-cover" class="headerlink" title="background-size:cover"></a>background-size:cover</h2><blockquote><p>background-size 有多种属性值,可以直接查看<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/background-size" target="_blank" rel="external">MDN</a>文档</p></blockquote><p>该属性可以使图片正好铺满相应的DIV,最难得的是,它是按照<em>图片较小的那一边来做缩放</em>的, 这是我们一直追寻的效果!</p><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></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.avator</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">'somewhere.png'</span>);</span><br><span class="line"> <span class="attribute">background-size</span>: cover;</span><br><span class="line"> <span class="attribute">background-position</span>: center</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>喜悦归喜悦,这种方式有一个问题:如何动态设置这个背景呢?<br>如果是IMG标签,我们设置src就好了,对于DIV难道我们每次都生成一个div然后强制重写 style <code>{background-image: url('new.png')}</code>? 如果是以前我也觉得这样写很变扭,但是接触了react一阵子,这种方式看起来还蛮正常的。。=_=。</p><h2 id="React-中比较“自然”的实现"><a href="#React-中比较“自然”的实现" class="headerlink" title="React 中比较“自然”的实现"></a>React 中比较“自然”的实现</h2><p>React中会出现很多的直接将style写入component里面去的地方,这样的好处不少,比如可以独立的发布,更快的渲染速度。按照这种模式,<code>style</code> 样式其实和普通的JS一样,是可编程的!这就为我们动态设置 <code>background-image</code> 打开了大门。</p><p>实现如下:</p><script async src="https://jsfiddle.net/nickday/ogu4jmqb/7/embed/js,html,result/"></script><p><strong>background-position:center</strong><br>针对长度大于宽度,或者宽度大于长度的照片,<code>background-size:cover</code> 都可以帮我们很好的布局。值得注意的是,这里面还有一个属性 <code>background-position:center</code>, 没有它的话,图片是从左向右,从上到下放置的,有了center后,布局才算美好。</p><h2 id="后话"><a href="#后话" class="headerlink" title="后话"></a>后话</h2><p>通过 <code>background-size:cover</code> 不只是可以生成圆形图片,实际上,任何需要这种铺满的效果都可以用到它。当然考虑到兼容性,它对IE8是无效的;)</p>]]></content>
<summary type="html">
<p>可以说,但凡涉及到用户信息展示的页面,用户自定义的头像总是让人记得最深刻,我们也总在这上面花去不少心思。从正方形,到圆角,在到现在的全圆,我们的头像展示也经历着自己的演进。之前我在处理个人头像的时候,想到的总是使用一个DIV+IMG,DIV 正方形并使用50%的圆角,盖在图
</summary>
<category term="CSS React" scheme="http://codingfishman.github.io/tags/CSS-React/"/>
</entry>
<entry>
<title>React PropTypes 到底有多少类型</title>
<link href="http://codingfishman.github.io/2016/03/30/React_PropTypes_%E7%B1%BB%E5%9E%8B%E7%AC%94%E8%AE%B0/"/>
<id>http://codingfishman.github.io/2016/03/30/React_PropTypes_类型笔记/</id>
<published>2016-03-30T09:55:49.000Z</published>
<updated>2016-04-10T15:15:57.000Z</updated>
<content type="html"><![CDATA[<p>经常遇到需要PropTypes验证的时候,忘记了这个值该是什么,不得不去查一遍<a href="https://facebook.github.io/react/docs/reusable-components.html" target="_blank" rel="external">React官网</a>,我想还是先记下来好了。所有属性都遵从驼峰命名,因此如果只有一个单词,那么他就是小写的。</p><h4 id="布尔值-bool"><a href="#布尔值-bool" class="headerlink" title="布尔值 bool"></a>布尔值 bool</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bool: PropTypes.bool</span><br></pre></td></tr></table></figure><h4 id="函数-function"><a href="#函数-function" class="headerlink" title="函数 function"></a>函数 function</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">func: PropTypes.func</span><br></pre></td></tr></table></figure><h4 id="数字-包括-int-和-float"><a href="#数字-包括-int-和-float" class="headerlink" title="数字, 包括 int 和 float"></a>数字, 包括 int 和 float</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">number: PropTypes.number</span><br></pre></td></tr></table></figure><h4 id="字符串-string"><a href="#字符串-string" class="headerlink" title="字符串 string"></a>字符串 string</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">string: PropTypes.string</span><br></pre></td></tr></table></figure><h4 id="函数-function-1"><a href="#函数-function-1" class="headerlink" title="函数 function"></a>函数 function</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">func: PropTypes.func</span><br></pre></td></tr></table></figure><h4 id="React元素-element"><a href="#React元素-element" class="headerlink" title="React元素 element"></a>React元素 element</h4><p>包括我们自己写的各种React component, 以及常见的 {this.props.children}</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">element: PropTypes.element</span><br></pre></td></tr></table></figure><h4 id="数组-Array"><a href="#数组-Array" class="headerlink" title="数组 Array"></a>数组 Array</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">array: PropTypes.array</span><br></pre></td></tr></table></figure><h4 id="数组-Array-指定每个元素类型"><a href="#数组-Array-指定每个元素类型" class="headerlink" title="数组 Array, 指定每个元素类型"></a>数组 Array, 指定每个元素类型</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 指定数组中的元素类型为 number</span></span><br><span class="line">array: PropTypes.arrayOf(PropTypes.number)</span><br></pre></td></tr></table></figure><h4 id="对象-object"><a href="#对象-object" class="headerlink" title="对象 object"></a>对象 object</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">obj: PropTypes.object</span><br></pre></td></tr></table></figure><h4 id="对象-object,-指定每个值的类型"><a href="#对象-object,-指定每个值的类型" class="headerlink" title="对象 object, 指定每个值的类型"></a>对象 object, 指定每个值的类型</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 指定该对象的值为number</span></span><br><span class="line">obj: PropTypes.objectOf(PropTypes.number)</span><br></pre></td></tr></table></figure><h4 id="对象-object,-指定对象结构"><a href="#对象-object,-指定对象结构" class="headerlink" title="对象 object, 指定对象结构"></a>对象 object, 指定对象结构</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><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">/*</span><br><span class="line">object: {</span><br><span class="line"> name: 'name',</span><br><span class="line"> password: 'password'</span><br><span class="line">}</span><br><span class="line">*/</span></span><br><span class="line">obj: PropTypes.shape({</span><br><span class="line"> <span class="attr">name</span>: PropTypes.string,</span><br><span class="line"> <span class="attr">name</span>: PropTypes.string</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h4 id="同时指定多种类型"><a href="#同时指定多种类型" class="headerlink" title="同时指定多种类型"></a>同时指定多种类型</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><br><span class="line">oneOfTypes: React.PropTypes.oneOfType([</span><br><span class="line"> React.PropTypes.string,</span><br><span class="line"> React.PropTypes.number</span><br><span class="line"> ])</span><br></pre></td></tr></table></figure><h4 id="还可以限定属性的值"><a href="#还可以限定属性的值" class="headerlink" title="还可以限定属性的值"></a>还可以限定属性的值</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 限定enum的值只能为'news' 或者 'photos'</span></span><br><span class="line">enum: PropTypes.oneOf([<span class="string">'news'</span>, <span class="string">'photos'</span>])</span><br></pre></td></tr></table></figure><h4 id="自定义对象"><a href="#自定义对象" class="headerlink" title="自定义对象"></a>自定义对象</h4><blockquote><p>也可以在已有对象(class)的基础上,指定某PropTypes的值为该class的实例(instance)</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// import Message from ',somewhere/Message'</span></span><br><span class="line">classInstance: PropTypes.instanceOf(Message)</span><br></pre></td></tr></table></figure><h4 id="自定义验证规则"><a href="#自定义验证规则" class="headerlink" title="自定义验证规则"></a>自定义验证规则</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><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="comment">/**</span><br><span class="line">* 如果验证失败,应该直接"return" 一个 Error对象,不是”throw", 也不是 "console.error()"</span><br><span class="line">* @param</span><br><span class="line"> props 当前调用者所传入的所有属性和值的键值对</span><br><span class="line">* @param</span><br><span class="line"> propName 当前验证的属性名,此处为'custom'</span><br><span class="line"> @param</span><br><span class="line"> componentName 当前component的名称,也即是定义本属性的对象</span><br><span class="line">**/</span></span><br><span class="line">custom: <span class="function"><span class="keyword">function</span> (<span class="params">props, propName, componentName</span>) </span>{</span><br><span class="line"> <span class="comment">// 如果传入的属性中,属性'custom'的值不包括 ’matchme' , 则返回’验证失败‘</span></span><br><span class="line"> <span class="keyword">if</span> (!<span class="regexp">/matchme/</span>.test(props[propName])) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Validation failed!'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>经常遇到需要PropTypes验证的时候,忘记了这个值该是什么,不得不去查一遍<a href="https://facebook.github.io/react/docs/reusable-components.html" target="_blank" rel="exte
</summary>
<category term="react" scheme="http://codingfishman.github.io/tags/react/"/>
</entry>
<entry>
<title>记webpack与CSS在一起所遇到的奇怪现象</title>
<link href="http://codingfishman.github.io/2016/03/17/%E8%AE%B0webpack%E4%B8%8ECSS%E5%9C%A8%E4%B8%80%E8%B5%B7%E6%89%80%E9%81%87%E5%88%B0%E7%9A%84%E5%A5%87%E6%80%AA%E7%8E%B0%E8%B1%A1/"/>
<id>http://codingfishman.github.io/2016/03/17/记webpack与CSS在一起所遇到的奇怪现象/</id>
<published>2016-03-17T14:27:00.000Z</published>
<updated>2016-03-17T15:55:32.000Z</updated>
<content type="html"><![CDATA[<p>自从第一次知道React的那天起,每次的meetup上都有他的身影,中间几次了解又几次放弃,但是每次遇到他的主题还是会忍不住去听。我和文博一直抱怨,angular最近真是没人聊啊,难道大家的生产环境已经都是React了喂?不过最近遇到一个项目,有一个需要特别频繁被移动设备访问的页面,我的第一想法便是如何加快速度,减少资源大小,后来就想用React吧,于是顺便也用起了webpack。</p><p>我并没有从零开始,而是在Github上找了一个<a href="https://github.com/davezuko/react-redux-starter-kit" target="_blank" rel="external">starter-kit</a>,感觉这个比较适合我,只有基本的webpack+react+react-router+redux, 我觉得目前我也只能先把这些搞懂。</p><p>在使用中经历了不少的痛楚,以后可以总结,但是有两个与css编译有关的奇怪现象,我觉得还是有必要先记下来</p><h1 id="keyframes被默认为-local-作用域"><a href="#keyframes被默认为-local-作用域" class="headerlink" title="@keyframes被默认为 :local 作用域"></a>@keyframes被默认为 :local 作用域</h1><p>这样的一段代码</p><figure class="highlight"><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">/* core.scss, 编译主入口 */</span><br><span class="line">:global {</span><br><span class="line"> @import 'base';</span><br><span class="line">}</span><br></pre></td></tr></table></figure><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><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* base.scss */</span></span><br><span class="line">@<span class="keyword">keyframes</span> animation1 {</span><br><span class="line"> 0% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(0deg);</span><br><span class="line"> }</span><br><span class="line"> 50% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(180deg);</span><br><span class="line"> }</span><br><span class="line"> 100% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(360deg);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.rotate</span> {</span><br><span class="line"> <span class="attribute">animation</span>: animation1 <span class="number">1s</span> infinite ease-in-out;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>被webpack编译后,会变成类似如下形式</p><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="keyword">keyframes</span> core_animation1_local_abcdef {</span><br><span class="line"> 0% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(0deg);</span><br><span class="line"> }</span><br><span class="line"> 50% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(180deg);</span><br><span class="line"> }</span><br><span class="line"> 100% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(360deg);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.rotate</span> {</span><br><span class="line"> <span class="attribute">animation</span>: animation1 <span class="number">1s</span> infinite ease-in-out;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以看到,<code>keyframes animation</code> 被重命名了,这其实是为了方便实现css namespace。 我一时间不是很明白,因为既然把animation重命名了,怎么不把<code>.rotate</code>中的<code>animation-name</code>也给重命名掉呢。其实在css-loader的Github主页测试用例中,他们有关于<a href="https://github.com/webpack/css-loader/blob/master/test/moduleTestCases/keyframes-and-animation/source.css" target="_blank" rel="external">keyframes and animations</a>的测试用例,里面是可以将<em>animation-name</em>给重命名掉的。</p><p>后来我注意到命名后,keyframes带有’local’关键字,难道说默认应该是local? 于是我把<code>.rotate</code>加上了<code>:local</code>关键字</p><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></pre></td><td class="code"><pre><span class="line"><span class="selector-pseudo">:local(.rotate)</span> {</span><br><span class="line"> <span class="attribute">animation</span>: animation1 <span class="number">1s</span> infinite ease-in-out;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果,在编译期就报错,提示<code>:local</code>不能包裹在<code>:global</code>里面,回头看才发现该文件<em>base.css</em>是被<em>core.scss</em>引入的,而且是在<code>:global</code>中引入。那么将<code>keyframes</code>再显示声明为<code>:global</code>呢?</p><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></pre></td><td class="code"><pre><span class="line">@<span class="keyword">keyframes</span> :global(animation1) {</span><br><span class="line"> 0% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(0deg);</span><br><span class="line"> }</span><br><span class="line"> 50% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(180deg);</span><br><span class="line"> }</span><br><span class="line"> 100% {</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">rotate</span>(360deg);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果没有重命名了,问题解决。奇怪的是为什么这里的<code>@keyframes</code>会被当做是<code>:local</code>的?</p><h1 id="font-face编译后内容生成了重复的花括号"><a href="#font-face编译后内容生成了重复的花括号" class="headerlink" title="@font-face编译后内容生成了重复的花括号{}"></a>@font-face编译后内容生成了重复的花括号{}</h1><p>由于项目使用了<a href="http://www.material-ui.com/" target="_blank" rel="external">Material-UI</a>,所以想引入Robot字体文件。自己从网络上下载了Roboto自己放在项目文件夹下,并配置如下</p><figure class="highlight"><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><br><span class="line">/* core.scss */</span><br><span class="line">:global{</span><br><span class="line"> @import 'base';</span><br><span class="line">};</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 还是在base.scss中,被core.scss @import */</span></span><br><span class="line">@<span class="keyword">font-face</span> {</span><br><span class="line"> <span class="attribute">font-family</span>: <span class="string">'Roboto'</span>;</span><br><span class="line"> <span class="attribute">src</span>: <span class="built_in">url</span>(<span class="string">'../static/fonts/RobotoCondensed-Light.ttf'</span>) <span class="built_in">format</span>(<span class="string">'truetype'</span>);</span><br><span class="line"> <span class="attribute">font-weight</span>: <span class="number">400</span>;</span><br><span class="line"> <span class="attribute">font-style</span>: normal;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是编译后字体一直没有生效,查看编译后的源代码,发现<code>@font-face</code>被编译成了这样→_→</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@font-face{{font-family: 'Roboto';src: url('../static/fonts/RobotoCondensed.ttf') format('truetype');font-weight: 400;font-style: normal;}}</span><br></pre></td></tr></table></figure><p>也就是说,编译后的<code>@font-face</code>被包裹上了<strong>两层花括号{}</strong>。很奇怪,后来也是经历了长时间的谷歌,都忘了从哪得到了这个提示,将<strong>@font-face</strong>放到整个文件的前面,包括<em>:global</em></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">/* core.scss, 编译入口 */</span><br><span class="line">@font-face {</span><br><span class="line"> font-family: 'Roboto';</span><br><span class="line"> src: url('../static/fonts/RobotoCondensed-Light.ttf') format('truetype');</span><br><span class="line"> font-weight: 400;</span><br><span class="line"> font-style: normal;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">:global{</span><br><span class="line"> @import 'base';</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>这样<strong>@font-face</strong>终于生效了。</p>]]></content>
<summary type="html">
<p>自从第一次知道React的那天起,每次的meetup上都有他的身影,中间几次了解又几次放弃,但是每次遇到他的主题还是会忍不住去听。我和文博一直抱怨,angular最近真是没人聊啊,难道大家的生产环境已经都是React了喂?不过最近遇到一个项目,有一个需要特别频繁被移动设备访
</summary>
<category term="webpack css css-modules" scheme="http://codingfishman.github.io/tags/webpack-css-css-modules/"/>
</entry>
<entry>
<title>Angular Http Interceptor 的应用笔记</title>
<link href="http://codingfishman.github.io/2016/03/13/Angular_Interceptor%E7%9A%84%E5%AE%9E%E9%99%85%E4%BD%BF%E7%94%A8/"/>
<id>http://codingfishman.github.io/2016/03/13/Angular_Interceptor的实际使用/</id>
<published>2016-03-13T13:34:53.000Z</published>
<updated>2016-03-13T15:33:59.000Z</updated>
<content type="html"><![CDATA[<p>之前做Java的时候,一旦遇到要登录拦截的情形,总会用到Filter。请求在到达controller之前,先经过filter,进行登录验证,然后再决定放行。前端开发也会有类似的场景,Http请求中通用的附加处理就是一种,而Angular原生的interceptor已经提供了支持。我在工作中也有一些用到它的地方,于是上来做个笔记。</p><h2 id="HttpProvider和它的Interceptor"><a href="#HttpProvider和它的Interceptor" class="headerlink" title="HttpProvider和它的Interceptor"></a>HttpProvider和它的Interceptor</h2><p>Angular的HttpProvider原生支持拦截器(interceptor),可以在请求发出和被接受之前做相应处理。在官网的API文档介绍中,有如下示例</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// register the interceptor as a service</span></span><br><span class="line">$provide.factory(<span class="string">'myHttpInterceptor'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">$q, dependency1, dependency2</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="comment">// optional method</span></span><br><span class="line"> <span class="string">'request'</span>: <span class="function"><span class="keyword">function</span>(<span class="params">config</span>) </span>{</span><br><span class="line"> <span class="comment">// do something on success</span></span><br><span class="line"> <span class="keyword">return</span> config;</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// optional method</span></span><br><span class="line"> <span class="string">'requestError'</span>: <span class="function"><span class="keyword">function</span>(<span class="params">rejection</span>) </span>{</span><br><span class="line"> <span class="comment">// do something on error</span></span><br><span class="line"> <span class="keyword">if</span> (canRecover(rejection)) {</span><br><span class="line"> <span class="keyword">return</span> responseOrNewPromise</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $q.reject(rejection);</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// optional method</span></span><br><span class="line"> <span class="string">'response'</span>: <span class="function"><span class="keyword">function</span>(<span class="params">response</span>) </span>{</span><br><span class="line"> <span class="comment">// do something on success</span></span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// optional method</span></span><br><span class="line"> <span class="string">'responseError'</span>: <span class="function"><span class="keyword">function</span>(<span class="params">rejection</span>) </span>{</span><br><span class="line"> <span class="comment">// do something on error</span></span><br><span class="line"> <span class="keyword">if</span> (canRecover(rejection)) {</span><br><span class="line"> <span class="keyword">return</span> responseOrNewPromise</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $q.reject(rejection);</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">$httpProvider.interceptors.push(<span class="string">'myHttpInterceptor'</span>);</span><br></pre></td></tr></table></figure><p>实际中我主要用到过 <em>request</em> 、 <em>response</em> 和 <em>responseError</em>。</p><h2 id="Request使用"><a href="#Request使用" class="headerlink" title="Request使用"></a>Request使用</h2><h3 id="API请求路径的通用修正"><a href="#API请求路径的通用修正" class="headerlink" title="API请求路径的通用修正"></a>API请求路径的通用修正</h3><p>也不知道为什么,从接触到interceptor的那天起,第一个想到的场景就是ajax请求中的URL。在最开始写ajax的时候,借助的是jQuery, 请求的URL直接放到调用参数中去,觉得这一切,怎么说呢,还挺好的哈?但随着前后端的分离,后端工程师在开发或后面维护API的过程中,或者由于其他的原因,URL会发生变更。拿我的经历来说,后端是一个独立的系统,同时服务于APP和web,后端的API命名是和业务直接关联的,在命名上,只会停留在 <code>/user/login</code>, 而我们在调用的时候,这样是不太直观的,所以会变成 <code>/api/user/login</code>,将来也有可能变成 <code>/somethingelse/user/login</code>。</p><p>而这样做还有一个好处,就是方便Nginx做跳转。通过proxy_pass, 将所有 <code>/api/</code> 的请求代理到真实的后台API。</p><blockquote><p>后台API的URL甚至可能有端口,但这一切都被Nginx处理掉了,对于前后段分离来说,这样既可以避免跨域请求,又可以让请求显得更加规范</p></blockquote><h3 id="封装共有请求参数"><a href="#封装共有请求参数" class="headerlink" title="封装共有请求参数"></a>封装共有请求参数</h3><p>这个是比较常见的,在和后台请求过程中除了每个API单独的参数,还会附带一些基本参数,比如认证的token(对于基于Token而不是Session的后端)和用于统计的其他信息。如果是每个单独加相信大家都不会愿意,于是我们可能写一个共有的方法,在里面附件共有参数(也可以更改API的请求路径),这些不借助Angular也可以做到,只是我们要记得每次都调用这个公用方法。</p><h3 id="设置请求超时时间"><a href="#设置请求超时时间" class="headerlink" title="设置请求超时时间"></a>设置请求超时时间</h3><p>为ajax请求设置通用的超时时间,而且还可以为上传请求单独配置更长的超时时间</p><p>这些都可以放到 <code>request</code> 中来,如下代码是只是一个例子:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">angular.module(<span class="string">'app.common'</span>)</span><br><span class="line"> .factory(<span class="string">'RequestInterceptor'</span>, RequestInterceptor)</span><br><span class="line"></span><br><span class="line"> RequestInterceptor.$inject = [<span class="string">'$rootScope'</span>, <span class="string">'Constants'</span>]</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">RequestInterceptor</span>(<span class="params">$rootScope, Constants</span>) </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//过滤掉所有类似 /static/**/** 的路径,只对API请求作过滤</span></span><br><span class="line"> <span class="keyword">var</span> _notStaticUrlReg = <span class="regexp">/^\/(?!static\/).*$/i</span></span><br><span class="line"> <span class="keyword">var</span> interceptor = {</span><br><span class="line"> <span class="string">'request'</span>: <span class="function"><span class="keyword">function</span>(<span class="params">config</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (config.url.match(_notStaticUrlReg)) {</span><br><span class="line"> <span class="keyword">var</span> data = config.data</span><br><span class="line"> <span class="keyword">if</span> (!data) {</span><br><span class="line"> data = config.data = {}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//重新组装API,将所有的请求加入API的项目基本路径,如果已经包含路径则不再添加</span></span><br><span class="line"> <span class="comment">//from '/user/login' TO '/api/url/login'</span></span><br><span class="line"> <span class="keyword">if</span> (config.url && config.url.indexOf(Constants.ApiBasePath) < <span class="number">0</span>) {</span><br><span class="line"> config.url = Constants.ApiBasePath + config.url</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.authentication = {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 设置请求超时时间</span></span><br><span class="line"> config.headers[<span class="string">'cache-control'</span>] = <span class="string">'no-cache'</span></span><br><span class="line"> config.timeout = <span class="number">30000</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//如果是上传api,超时时间加长</span></span><br><span class="line"> <span class="keyword">if</span> (config.url.indexOf(<span class="string">'/file/upload'</span>) > <span class="number">-1</span>) {</span><br><span class="line"> config.timeout = <span class="number">100000</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> config</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> interceptor</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h2 id="Response的使用"><a href="#Response的使用" class="headerlink" title="Response的使用"></a>Response的使用</h2><h3 id="通用的请求错误判断"><a href="#通用的请求错误判断" class="headerlink" title="通用的请求错误判断"></a>通用的请求错误判断</h3><p>如果在请求过程中认证信息过期(这个其实是可以避免的,在发起ajax请求前的页面应该做hasLogin的验证,目前没有这个验证才加上来),会返回403的错误码,或者一些其他代表认证信息过期的错误码,在response中通过这个来进行判断。</p><p>在上面的代码基础上,增加 <code>response: function ()</code> 模块</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> interceptor = {</span><br><span class="line"> <span class="string">'request'</span>: <span class="function"><span class="keyword">function</span> (<span class="params">config</span>) </span>{}, <span class="comment">// 此处省略</span></span><br><span class="line"> <span class="string">'response'</span>: <span class="function"><span class="keyword">function</span> (<span class="params">_response</span>) </span>{ <span class="comment">// **为和不就叫response?此处有坑**</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> status = _response.status</span><br><span class="line"> <span class="keyword">if</span> (status === <span class="number">403</span> || status === <span class="number">401</span>) {</span><br><span class="line"> <span class="keyword">var</span> _path = $location.path()</span><br><span class="line"> $rootScope.$emit(<span class="string">'root.needsLogin'</span>)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">var</span> url = _response.config.url</span><br><span class="line"> <span class="keyword">var</span> data = _response.data</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 依然只对api做处理,静态资源的URL不做处理</span></span><br><span class="line"> <span class="keyword">if</span> (url && url.match(_notStaticUrlReg)) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果请求过程中发现信息过期,广播该事件</span></span><br><span class="line"> <span class="keyword">if</span> (data && data.code === <span class="string">'auth_failed'</span> ) {</span><br><span class="line"> $rootScope.$emit(<span class="string">'root.needsLogin'</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">return</span> _response</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p><strong>_response 的坑</strong><br>前面提到过,在response的参数命名中为何不直接取名<code>response</code> 就好呢?这是在使用 <a href="https://github.com/danialfarid/ng-file-upload)" target="_blank" rel="external">ng-file-upload-shim</a>的时候遇到的。 <em>ng-file-upload-shim</em> 在IE8、IE9下会通过 <a href="https://github.com/mailru/FileAPI" target="_blank" rel="external">FileAPI</a> 调用Flash来上传,可喜的是,这个请求仍然会被Angular的http处理,故而interceptor也是可以用在这里的。但是我遇到了一个奇怪的问题,在IE上面,FileAPI的请求,其response(拦截器中的参数)为<strong>undefined</strong>,然而实际上后台是有返回的,而且FileAPI其自己也可以获取到。但是断点进去,response就是undefined。但我看到debug面板老是出现”_response”的变量,以为是浏览器自己的重命名。抱着试一试的态度,我把参数改成_response,有值了! 难道在这时候,response变量和浏览器发生了冲突,被浏览器直接写为undefined?</p></blockquote><h2 id="ResponseError的使用"><a href="#ResponseError的使用" class="headerlink" title="ResponseError的使用"></a>ResponseError的使用</h2><h3 id="自动重新请求"><a href="#自动重新请求" class="headerlink" title="自动重新请求"></a>自动重新请求</h3><p>最开始需要用到这个的时候,是因为一个莫名奇妙的<em>请求被丢弃</em>错误,一个普通的ajax请求,在某一台手机上,经常莫名奇妙的发生请求被丢弃故而报错,其请求完全没有到达超时的限制(请求超时后会被丢弃),尝试过不少但都没能找到根本原因,最后想到了一个暴力的方法,自动重新请求!</p><p>通过判断返回的<strong>状态码是否等于0</strong> 来判断该请求是否被丢弃,重试指定的次数。这个次数放在哪呢?我是放在response的config对象中,方便每次都能得到上一次的状态,而且只和这个response有关。</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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 还是接上面的构造代码,只是加入 `responseError:function() {}`</span></span><br><span class="line"> <span class="keyword">var</span> interceptor = {</span><br><span class="line"> <span class="string">'request'</span>: <span class="function"><span class="keyword">function</span> (<span class="params">config</span>) </span>{}, <span class="comment">// 此处省略</span></span><br><span class="line"> <span class="string">'response'</span>: <span class="function"><span class="keyword">function</span> (<span class="params">_response</span>) </span>{}, <span class="comment">// 此处省略</span></span><br><span class="line"> <span class="string">'responseError'</span>: <span class="function"><span class="keyword">function</span> (<span class="params">_response</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (_response.status === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> abortingHandler(_response)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//对aborted的request 进行重新请求</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">abortingHandler</span>(<span class="params">_response</span>) </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//如果是静态资源,则不作处理</span></span><br><span class="line"> <span class="keyword">if</span> (!_response.config.url.match(_notStaticUrlReg)) {</span><br><span class="line"> <span class="keyword">return</span> $q.reject(_response)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> $http = $injector.get(<span class="string">'$http'</span>)</span><br><span class="line"> <span class="keyword">var</span> _config = _response.config</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> _config.triedTimes !== <span class="string">'undefined'</span>) {</span><br><span class="line"> _config.triedTimes++</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> _config.triedTimes = <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (_config.triedTimes < <span class="number">3</span>) {</span><br><span class="line"> <span class="keyword">return</span> $http(_config)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> $q.reject(_response)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h2 id="后话"><a href="#后话" class="headerlink" title="后话"></a>后话</h2><p>在实践 angular interceptor的过程中,这篇博客帮助了我很多,推荐大家可以去看看, <a href="http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/" target="_blank" rel="external">传送门</a>。以上只是我自己用的一些简单配置,如有局限或不对之处,还请见谅和指正。</p><p>最近也开始使用react,感觉虽然react自己说学习他只需要一个react,可是在实际项目,尤其是企业级的项目中,只靠react一个是远远不够的。这时候,redux,react-router放上来都是最基本的配置。然后在实际过程中,对于类似angular interceptor的内容,我用的就是一个共有方法封装=_=,对于Http Mock使用的是<a href="https://github.com/jakerella/jquery-mockjax" target="_blank" rel="external">mockjax</a>。</p>]]></content>
<summary type="html">
<p>之前做Java的时候,一旦遇到要登录拦截的情形,总会用到Filter。请求在到达controller之前,先经过filter,进行登录验证,然后再决定放行。前端开发也会有类似的场景,Http请求中通用的附加处理就是一种,而Angular原生的interceptor已经提供了
</summary>
<category term="angular javascript" scheme="http://codingfishman.github.io/tags/angular-javascript/"/>
</entry>
<entry>
<title>flex布局与float的冲突</title>
<link href="http://codingfishman.github.io/2016/02/21/flex%E5%B8%83%E5%B1%80%E4%B8%8Efloat%E7%9A%84%E5%86%B2%E7%AA%81/"/>
<id>http://codingfishman.github.io/2016/02/21/flex布局与float的冲突/</id>
<published>2016-02-21T13:11:12.000Z</published>
<updated>2016-02-25T01:09:01.000Z</updated>
<content type="html"><![CDATA[<p>2015年的某一天,我在某外企当码农,用户对浏览器的使用不像我们大天朝,因此心情上轻松了不少。有一次,我和文博同志接到了一个新网站的任务,我无意中(还是经文博的提起后?)在<a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/" target="_blank" rel="external">CSS-Tricks</a>上看到了一篇关于Flexbox的入门介绍,发现这和之前接触的float+margin+padding的布局大不一样,我想,那我们就用flex布局吧!这是第一次接触并使用到Flex布局,可以说用得很浅。</p><p>在后来一次的工作中,我遇到了这样一个很普通的场景,同一行的DIV,有两块:左和右,我自然地通过 <code>float:right</code> 来将DIV右到了右边。</p><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></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">300px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.item</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">50px</span>;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">7px</span> <span class="number">15px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.right</span> {</span><br><span class="line"> <span class="attribute">float</span>: right;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"item"</span>></span></span><br><span class="line"> left part</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"item right"</span>></span></span><br><span class="line"> right part</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>然而事情发生了转折,left-div 和 right-div 的高度是不一样的(字体大小不一样),但是在同一行上都需要居中对齐,我于是想到了flex布局。<br>通过 <code>flex:space-between</code> 将元素分别漂流到了两遍,并设置 <code>align-items:center</code> 来实现垂直居中, 示例如下:</p><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></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">400px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">300px</span>;</span><br><span class="line"> <span class="attribute">display</span>: -webkit-box;</span><br><span class="line"> <span class="attribute">display</span>: space-between;</span><br><span class="line"> <span class="attribute">-webkit-box-pack</span>: justify;</span><br><span class="line"> <span class="attribute">jusity-content</span>: space-between;</span><br><span class="line"> <span class="attribute">-webkit-box-align</span>: center;</span><br><span class="line"> <span class="attribute">align-items</span>: center;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><script async src="https://jsfiddle.net/nickday/fayzaew2/11/embed/html,css,result/dark/"></script><p><strong>但是我忘记将里面的 <code>.right</code> 样式移除</strong>,而且在chrome之下,其表现是很正常的,我也以为事情完成了。</p><p>可是在后面真实的手机测试中,尤其是比较低版本系统的安卓手机(Android 4.1.1, webkit默认浏览器),我们发现<strong>right-part</strong>这一块的DIV,直接消失了! 经过debuggap的调试,我发现<code>float:right;</code> 一旦拿掉,DIV就回来了。</p><p>注:邧一峰老师的flex介绍写得也很适合我们,附上链接 <a href="http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html" target="_blank" rel="external">flex布局教程</a></p>]]></content>
<summary type="html">
<p>2015年的某一天,我在某外企当码农,用户对浏览器的使用不像我们大天朝,因此心情上轻松了不少。有一次,我和文博同志接到了一个新网站的任务,我无意中(还是经文博的提起后?)在<a href="https://css-tricks.com/snippets/css/a-guid
</summary>
<category term="CSS3 flex Mobile" scheme="http://codingfishman.github.io/tags/CSS3-flex-Mobile/"/>
</entry>
<entry>
<title>禁用手机触屏时那一瞬间的背景色</title>
<link href="http://codingfishman.github.io/2016/02/21/%E7%A6%81%E7%94%A8%E6%89%8B%E6%9C%BA%E8%A7%A6%E5%B1%8F%E6%97%B6%E9%82%A3%E4%B8%80%E7%9E%AC%E9%97%B4%E7%9A%84%E8%83%8C%E6%99%AF%E8%89%B2/"/>
<id>http://codingfishman.github.io/2016/02/21/禁用手机触屏时那一瞬间的背景色/</id>
<published>2016-02-21T12:44:35.000Z</published>
<updated>2016-02-24T16:26:25.000Z</updated>
<content type="html"><![CDATA[<p>当我们的H5页面是服务于APP的,也就是内嵌在APP里面使用时,我们总是希望H5表现得和原生APP一样。前不久,我遇到一个页面里面有很多的展开项,默认隐藏;当点击某个展开项的时候,隐藏的DIV显示出来。然而在点击展开栏的那一瞬间,浏览器总是会为该DIV的背景加上背景色,有些碍眼,出卖了他不是APP的本质。</p><p>其实这个是可以通过CSS来禁用的,老实说,真要和浏览器打交道的时候,CSS才是最懂它的那个。禁用点击时默认背景效果的代码如下:</p><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></pre></td><td class="code"><pre><span class="line">* {</span><br><span class="line"><span class="attribute">-webkit-tap-highlight-color</span>: <span class="built_in">rgba</span>(0, 0, 0, 0);</span><br><span class="line"><span class="attribute">-moz-tap-highlight-color</span>: <span class="built_in">rgba</span>(0, 0, 0, 0);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样,当我们在手机上点击网页元素的时候,都不会有那一抹背景了。</p><p>参考 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-tap-highlight-color" target="_blank" rel="external">MDN</a></p>]]></content>
<summary type="html">
<p>当我们的H5页面是服务于APP的,也就是内嵌在APP里面使用时,我们总是希望H5表现得和原生APP一样。前不久,我遇到一个页面里面有很多的展开项,默认隐藏;当点击某个展开项的时候,隐藏的DIV显示出来。然而在点击展开栏的那一瞬间,浏览器总是会为该DIV的背景加上背景色,有些
</summary>
<category term="CSS CSS3 H5 Mobile" scheme="http://codingfishman.github.io/tags/CSS-CSS3-H5-Mobile/"/>
</entry>
<entry>
<title>firstblog</title>
<link href="http://codingfishman.github.io/2016/02/21/firstblog/"/>
<id>http://codingfishman.github.io/2016/02/21/firstblog/</id>
<published>2016-02-21T09:37:48.000Z</published>
<updated>2016-02-21T12:42:46.000Z</updated>
<content type="html"><![CDATA[<p>这是在github.io上的第一篇日志,希望以后会越来越多。</p>]]></content>
<summary type="html">
<p>这是在github.io上的第一篇日志,希望以后会越来越多。</p>
</summary>
<category term="日记" scheme="http://codingfishman.github.io/tags/%E6%97%A5%E8%AE%B0/"/>
</entry>
</feed>