-
Notifications
You must be signed in to change notification settings - Fork 0
/
rss.xml
355 lines (233 loc) · 370 KB
/
rss.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>前端博客</title>
<link>https://www.noonme.com/</link>
<atom:link href="/rss.xml" rel="self" type="application/rss+xml"/>
<description>专职前端开发,目前在中国北京。这个博客是我随便记录东西的地方,分享最新的前端开发技术。</description>
<pubDate>Thu, 19 Sep 2019 08:05:18 GMT</pubDate>
<generator>http://hexo.io/</generator>
<item>
<title>Digital Ocean开启TCP-BBR拥塞控制算法</title>
<link>https://www.noonme.com/post/2017/08/digital-ocean-bbr/</link>
<guid>https://www.noonme.com/post/2017/08/digital-ocean-bbr/</guid>
<pubDate>Tue, 29 Aug 2017 02:47:23 GMT</pubDate>
<description>
<p>这个新的TCP拥塞控制算法——BBR已经火了一段时间了,优势可以参见 <a href="https://www.zhihu.com/question/53559433" target="_blank" rel="noopener">Linux Kernel 4.9 中的 BBR 算法与之前的 TCP 拥塞控制相比有什么优势?</a>,里面说的已经非常全面了。在Digital Ocean的CentOS7系统的VPS上开启BBR记录如下。</p>
</description>
<content:encoded><![CDATA[<p>这个新的TCP拥塞控制算法——BBR已经火了一段时间了,优势可以参见 <a href="https://www.zhihu.com/question/53559433" target="_blank" rel="noopener">Linux Kernel 4.9 中的 BBR 算法与之前的 TCP 拥塞控制相比有什么优势?</a>,里面说的已经非常全面了。在Digital Ocean的CentOS7系统的VPS上开启BBR记录如下。</p><a id="more"></a><h2 id="2018-03-17">2018-03-17</h2><p>最近发现有别人写好的一键安装脚本可以一键安装<code>bbr</code>,并且有<code>bbr魔改</code>,据说效果更好。</p><p>一键安装脚本:<a href="https://github.com/chiakge/Linux-NetSpeed" target="_blank" rel="noopener">Linux-NetSpeed</a></p><p>关于速度的测试文章:<a href="https://lolico.moe/gotagota/compare-serverspeeder-and-bbr.html" target="_blank" rel="noopener">[小实验] 锐速&BBR究竟哪家强?个人PC有必要上锐速吗?</a></p><h2 id="更新内核">更新内核</h2><p>先更新系统</p><p></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> yum update -y</span></span><br></pre></td></tr></table></figure><p></p><p>安装内核,目前CentOS已经可以通过elrepo源来更新内核,目前最新内核版本为:<code>4.12.9</code></p><p></p><figure class="highlight shell"><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="meta">$</span><span class="bash"> rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> yum --enablerepo=elrepo-kernel install kernel-ml -y</span></span><br></pre></td></tr></table></figure><p></p><p>查看内核是否安装成功</p><p></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> awk -F\<span class="string">' '</span><span class="variable">$1</span>==<span class="string">"menuentry "</span> {<span class="built_in">print</span> i++ <span class="string">" : "</span> <span class="variable">$2</span>}<span class="string">' /etc/grub2.cfg</span></span></span><br></pre></td></tr></table></figure><p></p><p>出现类似返回</p><p></p><figure class="highlight plain"><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">0 : CentOS Linux 7 Rescue f16713269c69461db4addeffb3a94dc9 (4.12.9-1.el7.elrepo.x86_64)</span><br><span class="line">1 : CentOS Linux (4.12.9-1.el7.elrepo.x86_64) 7 (Core)</span><br><span class="line">2 : CentOS Linux (3.10.0-514.21.1.el7.x86_64) 7 (Core)</span><br><span class="line">3 : CentOS Linux (0-rescue-de820036e9b44905be9e94bc0f95cdc7) 7 (Core)</span><br></pre></td></tr></table></figure><p></p><p>把 CentOS Linux (4.12.9-1.el7.elrepo.x86_64) 设置成默认内核</p><p></p><figure class="highlight shell"><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="meta">#</span><span class="bash"> grub2-set-default 1 也可以</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> grub2-set-default 0</span></span><br></pre></td></tr></table></figure><p></p><p>一般情况下,这里就应该重启系统使使grub生效了,但由于Digital Ocean的限制,DO的用户要先在DO的后台把内核切换到GrubLoader v0.2(相当于自定义吧),然后再重启系统</p><p><img src="https://ws4.sinaimg.cn/large/006tKfTcly1fj0nhzsxfaj31kc0m0wj7.jpg" alt></p><p></p><figure class="highlight shell"><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="meta">#</span><span class="bash"> 在Digital Ocean后台将Kernel更换为GrubLoader v0.2</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> reboot</span></span><br></pre></td></tr></table></figure><p></p><p>查看内核是否更换成功</p><p></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> uname -r</span></span><br></pre></td></tr></table></figure><p></p><h2 id="开启bbr">开启BBR</h2><p>编辑<code>/etc/sysctl.conf</code>,加入如下内容</p><p></p><figure class="highlight plain"><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">net.core.default_qdisc = fq</span><br><span class="line">net.ipv4.tcp_congestion_control = bbr</span><br></pre></td></tr></table></figure><p></p><p>保存生效</p><p></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> sysctl -p</span></span><br></pre></td></tr></table></figure><p></p><p>查看当前内核TCP设置</p><p></p><figure class="highlight shell"><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="meta">$</span><span class="bash"> sysctl net.ipv4.tcp_available_congestion_control</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> sysctl net.ipv4.tcp_congestion_control</span></span><br></pre></td></tr></table></figure><p></p><p>如果结果都有<code>bbr</code>,则说明内核已开启BBR算法,执行:</p><p></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> lsmod | grep bbr</span></span><br></pre></td></tr></table></figure><p></p><p>显示<code>tcp_bbr</code>说明BBR已正常启动</p><p>至于用了BBR之后到底速度有多少改进呢,我个人其实并没有感觉到有特别显著的效果(网上传言的效果简直夸张)。而且Google的测试报告也并没有说适用于所有情况,所谓BBR代替CUBIC成为TCP默认的拥塞控制算法,大概还相当遥远吧。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2017/08/digital-ocean-bbr/#disqus_thread</comments>
</item>
<item>
<title>Homebrew国内加速</title>
<link>https://www.noonme.com/post/2017/03/homebrew-speed-up/</link>
<guid>https://www.noonme.com/post/2017/03/homebrew-speed-up/</guid>
<pubDate>Mon, 06 Mar 2017 03:10:58 GMT</pubDate>
<description>
<p>Mac搭配homebrew简直舒爽啊,然而homebrew托管在github,对国内用户来说不仅频频被墙,而且速度也不理想。今天笔者就告诉大家国内用户顺畅访问homebrew的方法。</p>
</description>
<content:encoded><![CDATA[<p>Mac搭配homebrew简直舒爽啊,然而homebrew托管在github,对国内用户来说不仅频频被墙,而且速度也不理想。今天笔者就告诉大家国内用户顺畅访问homebrew的方法。</p><a id="more"></a><h2 id="中科大的镜像">中科大的镜像</h2><p>中科大镜像比较稳定,而且速度不错。官方网站:<a href="http://mirrors.ustc.edu.cn/" target="_blank" rel="noopener">http://mirrors.ustc.edu.cn/</a>。搜索<code>brew</code>,然后点击<code>Help</code>即可查看用法:</p><p><strong>替换Homebrew默认源</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 替换brew.git:</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://mirrors.ustc.edu.cn/brew.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 替换homebrew-core.git:</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>/Library/Taps/homebrew/homebrew-core"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://mirrors.ustc.edu.cn/homebrew-core.git</span><br><span class="line"></span><br><span class="line">brew update</span><br></pre></td></tr></table></figure><p></p><p><strong>替换Homebrew Bottles源</strong></p><p>对于bash用户:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles'</span> >> ~/.bash_profile</span><br><span class="line"><span class="built_in">source</span> ~/.bash_profile</span><br></pre></td></tr></table></figure><p></p><p>对于zsh用户:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles'</span> >> ~/.zshrc</span><br><span class="line"><span class="built_in">source</span> ~/.zshrc</span><br></pre></td></tr></table></figure><p></p><h2 id="清华大学镜像">清华大学镜像</h2><p>也是比较靠谱的开源镜像。官方网站:<a href="https://mirror.tuna.tsinghua.edu.cn/help/homebrew/" target="_blank" rel="noopener">https://mirror.tuna.tsinghua.edu.cn/help/homebrew/</a>。</p><p><strong>替换Homebrew默认源</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 替换brew.git:</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 替换homebrew-core.git:</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>/Library/Taps/homebrew/homebrew-core"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git</span><br><span class="line"></span><br><span class="line">brew update</span><br></pre></td></tr></table></figure><p></p><p><strong>使用homebrew-science或者homebrew-python</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>/Library/Taps/homebrew/homebrew-science"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-science.git</span><br><span class="line"></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>/Library/Taps/homebrew/homebrew-python"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-python.git</span><br><span class="line"></span><br><span class="line">brew update</span><br></pre></td></tr></table></figure><p></p><p><strong>替换Homebrew Bottles源</strong></p><p>对于bash用户:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles'</span> >> ~/.bash_profile</span><br><span class="line"><span class="built_in">source</span> ~/.bash_profile</span><br></pre></td></tr></table></figure><p></p><p>对于zsh用户:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles'</span> >> ~/.zshrc</span><br><span class="line"><span class="built_in">source</span> ~/.zshrc</span><br></pre></td></tr></table></figure><p></p><h2 id="其它镜像">其它镜像</h2><h3 id="搬http-ban-ninja">搬<a href="http://ban.ninja/" target="_blank" rel="noopener">http://ban.ninja/</a></h3><p>此为Homebrew Bottles源,及下载二进制文件地址:</p><p>bash用户:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export HOMEBREW_BOTTLE_DOMAIN=http://7xkcej.dl1.z0.glb.clouddn.com'</span> >> ~/.bash_profile</span><br><span class="line"><span class="built_in">source</span> ~/.bash_profile</span><br></pre></td></tr></table></figure><p></p><p>对于zsh用户:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">'export HOMEBREW_BOTTLE_DOMAIN=http://7xkcej.dl1.z0.glb.clouddn.com'</span> >> ~/.zshrc</span><br><span class="line"><span class="built_in">source</span> ~/.zshrc</span><br></pre></td></tr></table></figure><p></p><h3 id="coding-homebrew">coding <a href="https://coding.net/u/homebrew/p/homebrew/git" target="_blank" rel="noopener">homebrew</a></h3><p>只有Homebrew源,没有二进制文件源。</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://git.coding.net/homebrew/homebrew.git</span><br><span class="line"></span><br><span class="line">brew update</span><br></pre></td></tr></table></figure><p></p><h2 id="重置homebrew默认源">重置Homebrew默认源</h2><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重置brew.git:</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://github.com/Homebrew/brew.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重置homebrew-core.git:</span></span><br><span class="line"><span class="built_in">cd</span> <span class="string">"<span class="variable">$(brew --repo)</span>/Library/Taps/homebrew/homebrew-core"</span></span><br><span class="line">git remote <span class="built_in">set</span>-url origin https://github.com/Homebrew/homebrew-core.git</span><br><span class="line"></span><br><span class="line">brew update</span><br></pre></td></tr></table></figure><p></p><h2 id="其它加速方法">其它加速方法</h2><p>除了通过替换源加速之外,如果你有自己的代理,也可以通过代理加速。</p><p>如socks代理配置:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">vi ~/.curlrc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 改成自己的代理端口</span></span><br><span class="line">socks5=127.0.0.1:1080</span><br></pre></td></tr></table></figure><p></p><p>或者通过<code>proxychains</code>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install proxychains-ng</span><br></pre></td></tr></table></figure><p></p><p>然后在<code>/usr/local/etc/proxychains.conf</code>配置代理信息,即在 [ProxyList] 下面(也就是末尾)加入代理类型,代理地址和端口,如:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">socks5 127.0.0.1 1080</span><br></pre></td></tr></table></figure><p></p><p>配置好之后就可以在需要使用代理的命令前加<code>proxychians</code>来加速:</p><p></p><figure class="highlight plain"><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">proxychians brew update</span><br><span class="line">proxychians brew install ...</span><br></pre></td></tr></table></figure><p></p>]]></content:encoded>
<comments>https://www.noonme.com/post/2017/03/homebrew-speed-up/#disqus_thread</comments>
</item>
<item>
<title>关于HTTP安全头你需要知道的一切[译]</title>
<link>https://www.noonme.com/post/2017/01/http-security-headers/</link>
<guid>https://www.noonme.com/post/2017/01/http-security-headers/</guid>
<pubDate>Fri, 20 Jan 2017 06:56:30 GMT</pubDate>
<description>
<p>28年前,一些物理学家需要一种方法来轻松分享实验数据,因此网络诞生了。这通常被认为是一个好举措。不辛的是所有物理学家触碰的是——从三角学到强大的核力——最终成为武器,HTTP协议也是如此。</p>
</description>
<content:encoded><![CDATA[<p>28年前,一些物理学家需要一种方法来轻松分享实验数据,因此网络诞生了。这通常被认为是一个好举措。不辛的是所有物理学家触碰的是——从三角学到强大的核力——最终成为武器,HTTP协议也是如此。</p><a id="more"></a><p>原文链接:<a href="https://blog.appcanary.com/2017/http-security-headers.html" target="_blank" rel="noopener">https://blog.appcanary.com/2017/http-security-headers.html</a></p><p>任何可以被攻击的都必须得到捍卫,而且通常所有的安全功能就是在一个螺丝钉上亡羊补牢...事情变得有点复杂。</p><p>本文解释了什么是安全头以及如何在<code>Rails</code>, <code>Django</code>, <code>Express.js</code>, <code>Go</code>, <code>Nginx</code>, 和 <code>Apache</code>设置这些安全头。</p><p>请注意,有些头部最好配置在你的HTTP服务端,然而有些头部最好设置在你的应用层。在这里请自由选择。你可以通过 Mozilla’s <a href="https://observatory.mozilla.org/analyze.html?host=w3cboy.com" target="_blank" rel="noopener">Observatory</a> 看看你配置的怎么样。</p><h2 id="http-security-headers">HTTP Security Headers</h2><ul><li><a href="#x-xss-protection">X-XSS-Protection</a></li><li><a href="#content-security-policy">Content Security Policy</a></li><li><a href="#http-strict-transport-security-hsts">HTTP Strict Transport Security (HSTS)</a></li><li><a href="#http-public-key-pinning-hpkp">HTTP Public Key Pinning (HPKP)</a></li><li><a href="#x-frame-options">X-Frame-Options</a></li><li><a href="#x-content-type-options">X-Content-Type-Options</a></li><li><a href="#referrer-policy">Referrer-Policy</a></li><li><a href="#cookie-options">Cookie Options</a></li></ul><h2 id="x-xss-protection">X-XSS-Protection</h2><p></p><figure class="highlight plain"><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">X-XSS-Protection: 0;</span><br><span class="line">X-XSS-Protection: 1;</span><br><span class="line">X-XSS-Protection: 1; mode=block</span><br></pre></td></tr></table></figure><p></p><h3 id="为什么?">为什么?</h3><p>跨站脚本,同常简称为XSS,是一种攻击者让网页加载一些恶意JavaScript的攻击方式。<code>X-XSS-Protection</code>是 Chrome 和 IE 浏览器的一个属性,用来抵御 “反射型” XSS攻击——攻击者发送恶意的有效载荷作为请求的一部分。</p><p><code>X-XSS-Protection: 0</code> 关闭XSS保护。</p><p><code>X-XSS-Protection: 1</code> 过滤请求里面的所有外部脚本,但是仍然会渲染页面。</p><p><code>X-XSS-Protection: 1; mode=block</code> 当触发的时候,将会阻断整个页面的渲染。</p><h3 id="我应该使用吗?">我应该使用吗?</h3><p>是的。设置<code>X-XSS-Protection: 1; mode=block</code> 这种 “过滤恶意脚本” 是有问题的;为什么请看<a href="http://blog.innerht.ml/the-misunderstood-x-xss-protection/" target="_blank" rel="noopener">这里</a>。</p><h3 id="如何使用?">如何使用?</h3><table><thead><tr><th style="text-align:center">平台</th><th style="text-align:center">用法</th></tr></thead><tbody><tr><td style="text-align:center">Rails 4 and 5</td><td style="text-align:center">默认开启</td></tr><tr><td style="text-align:center">Django</td><td style="text-align:center"><code>SECURE_BROWSER_XSS_FILTER = True</code></td></tr><tr><td style="text-align:center">Express.js</td><td style="text-align:center">用 <a href="https://helmetjs.github.io/docs/xss-filter/" target="_blank" rel="noopener">helmet</a></td></tr><tr><td style="text-align:center">Go</td><td style="text-align:center">用 <a href="https://github.com/unrolled/secure" target="_blank" rel="noopener">unrolled/secure</a></td></tr><tr><td style="text-align:center">Nginx</td><td style="text-align:center"><code>add_header X-XSS-Protection "1; mode=block";</code></td></tr><tr><td style="text-align:center">Apache</td><td style="text-align:center"><code>Header always set X-XSS-Protection "1; mode=block"</code></td></tr></tbody></table><h3 id="我想了解更多">我想了解更多</h3><p><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection" target="_blank" rel="noopener">X-XSS-Protection - MDN</a></p><h2 id="content-security-policy">Content Security Policy</h2><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Content-Security-Policy: <policy></span><br></pre></td></tr></table></figure><p></p><h3 id="为什么?-v2">为什么?</h3><p>Content Security Policy 可以被认为是上面header里更高版本的<code>X-XSS-Protection</code>。<code>X-XSS-Protection</code> 只会阻止来自请求中的脚本,但是并不会阻止涉及存储恶意脚本在你的服务器或者加载带有恶意脚本外部资源的XSS攻击。</p><p>CSP 给你一种语言去定义浏览器可以从哪些地方加载资源。你可以为脚本、图片、字体、样式表等设置细粒度的白名单源。你也可以将任何加载的内容与哈希或签名进行比较。</p><h3 id="我应该使用吗?-v2">我应该使用吗?</h3><p>是的。它虽然不会防止所有的XSS攻击,但是会显著缓解它们带来的影响,而且是深度防御的一个重要方面。如果你是个勇敢的读者,并且走在了前面,检查了<a href="https://appcanary.com/" target="_blank" rel="noopener">appcanary.com</a>的响应头,你会发现我们还没有设置 CSP。我们正在使用的一些Rails的开发插件阻碍了我们来设置CSP以至于会造成真正的安全影响。我们正在解决这个问题,并在下次连载写出来。</p><h3 id="如何使用?-v2">如何使用?</h3><p>书写CSP策略是具有挑战的。去<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy__by_cnvoid" target="_blank" rel="noopener">这里</a>看可以使用的所有指令列表。从<a href="https://csp.withgoogle.com/docs/adopting-csp.html" target="_blank" rel="noopener">这里</a>开始是个不错的选择。</p><table><thead><tr><th style="text-align:center">平台</th><th style="text-align:center">用法</th></tr></thead><tbody><tr><td style="text-align:center">Rails 4 and 5</td><td style="text-align:center">使用 <a href="https://github.com/twitter/secureheaders" target="_blank" rel="noopener">secureheaders</a></td></tr><tr><td style="text-align:center">Django</td><td style="text-align:center">使用 <a href="https://github.com/mozilla/django-csp" target="_blank" rel="noopener">django-csp</a></td></tr><tr><td style="text-align:center">Express.js</td><td style="text-align:center">使用 <a href="https://github.com/helmetjs/csp" target="_blank" rel="noopener">helmet/csp</a></td></tr><tr><td style="text-align:center">Go</td><td style="text-align:center">使用 <a href="https://github.com/unrolled/secure" target="_blank" rel="noopener">unrolled/secure</a></td></tr><tr><td style="text-align:center">Nginx</td><td style="text-align:center"><code>add_header Content-Security-Policy "<policy>";</code></td></tr><tr><td style="text-align:center">Apache</td><td style="text-align:center"><code>Header always set Content-Security-Policy "<policy>"</code></td></tr></tbody></table><h3 id="我想了解更多-v2">我想了解更多</h3><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy" target="_blank" rel="noopener">Content-Security-Policy - MDN</a></li><li><a href="https://content-security-policy.com/" target="_blank" rel="noopener">CSP Quick Reference Guide</a></li><li><a href="https://csp.withgoogle.com/docs/index.html" target="_blank" rel="noopener">Google’s CSP Guide</a></li></ul><h2 id="http-strict-transport-security-hsts">HTTP Strict Transport Security (HSTS)</h2><p></p><figure class="highlight plain"><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">Strict-Transport-Security: max-age=<expire-time></span><br><span class="line">Strict-Transport-Security: max-age=<expire-time>; includeSubDomains</span><br><span class="line">Strict-Transport-Security: max-age=<expire-time>; preload</span><br></pre></td></tr></table></figure><p></p><h3 id="为什么?-v3">为什么?</h3><p>当我们想安全地与人沟通,我们面临两个问题。第一个问题是隐私,我们想确保我们发送的消息只能由收件人读取,没有其他人。另一个问题是认证:我们如何知道收件人就是他们说的他们是谁?</p><p>HTTPS通过加密解决了第一个问题,但它在认证上有一些重大的问题(稍后会有更多说明,如 <a href="#http-public-key-pinning-hpkp">Public Key Pinning</a>)。HSTS头解决元问题:你如何知道与你对话的人实际上支持加密?</p><p>HSTS通过<a href="https://moxie.org/software/sslstrip/" target="_blank" rel="noopener">sslstrip</a>削弱攻击。假设你正在使用一个敌对的网络,一个恶意攻击者控制了WiFi路由。攻击者可以禁用您和您浏览的网站之间的加密。即使你访问的网站只能通过HTTPS,攻击者可以中间人的HTTP流量,使它看起来像网站的作品在未加密的HTTP。不需要SSL证书,只需禁用加密。</p><p>回到HSTS。<code>Strict-Transport-Security</code>头通过让你的浏览器知道它必须始终让你的网站使用加密来解决这个问题。只要你的浏览器识别到HSTS头,并且该HSTS头没过期,那么它就不允许在未加密的情况下访问到该站点,如果通过HTTPS无法访问它就会报出错误。</p><h3 id="我应该使用吗?-v3">我应该使用吗?</h3><p>是的。你的应用只能通过HTTPS访问,对吧?通过普通HTTP浏览将重定向到安全的网站,对吧?(提示:使用 <a href="https://letsencrypt.org/" target="_blank" rel="noopener">letsencrypt</a> 如果你想避开商业认证机构的敲诈。)</p><p>HSTS头的一个缺点是它允许一种<a href="http://www.radicalresearch.co.uk/lab/hstssupercookies" target="_blank" rel="noopener">巧妙的技术</a>创建可以指纹你用户的超级Cookies。</p><p>作为一个网站的经营者,你可能已经有点追踪你的用户了,但是请尽量使用HSTS的好处,不要为了超级Cookies去使用HSTS。</p><h3 id="如何使用?-v3">如何使用?</h3><p>有两种选择</p><ul><li><code>includeSubDomains</code> - HSTS 应用到子域名</li><li><code>preload</code> - 谷歌提供让你的网站在浏览器访问硬编码到HTTPS的<a href="https://hstspreload.org/" target="_blank" rel="noopener">服务</a>。通过这种方式,用户甚至不用访问你的网站:浏览器已经知道了它们应该阻止未加密的连接。从那个名单上去掉比较困难,所以除非你的子域名能够永远支持HTTPS你才打开它。</li></ul><table><thead><tr><th style="text-align:center">平台</th><th style="text-align:center">用法</th></tr></thead><tbody><tr><td style="text-align:center">Rails 4</td><td style="text-align:center"><code>config.force_ssl = true</code>默认不包括子域名,要设置的话:<code>config.ssl_options = { hsts: { subdomains: true } }</code></td></tr><tr><td style="text-align:center">Rails 5</td><td style="text-align:center"><code>config.force_ssl = true</code></td></tr><tr><td style="text-align:center">Django</td><td style="text-align:center"><code>SECURE_HSTS_SECONDS = 31536000</code> <code>SECURE_HSTS_INCLUDE_SUBDOMAINS = True</code></td></tr><tr><td style="text-align:center">Express.js</td><td style="text-align:center">使用 <a href="https://helmetjs.github.io/docs/hsts/" target="_blank" rel="noopener">helmet</a></td></tr><tr><td style="text-align:center">Go</td><td style="text-align:center">使用 <a href="https://github.com/unrolled/secure" target="_blank" rel="noopener">unrolled/secure</a></td></tr><tr><td style="text-align:center">Nginx</td><td style="text-align:center"><code>add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; ";</code></td></tr><tr><td style="text-align:center">Apache</td><td style="text-align:center"><code>Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains;</code></td></tr></tbody></table><h3 id="我想了解更多-v3">我想了解更多</h3><ul><li><a href="https://tools.ietf.org/html/rfc6797" target="_blank" rel="noopener">RFC 6797</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Security/HTTP_Strict_Transport_Security" target="_blank" rel="noopener">Strict-Transport-Security</a></li></ul><h2 id="http-public-key-pinning-hpkp">HTTP Public Key Pinning (HPKP)</h2><p></p><figure class="highlight plain"><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">Public-Key-Pins: pin-sha256=<base64==>; max-age=<expireTime>;</span><br><span class="line">Public-Key-Pins: pin-sha256=<base64==>; max-age=<expireTime>; includeSubDomains</span><br><span class="line">Public-Key-Pins: pin-sha256=<base64==>; max-age=<expireTime>; report-uri=<reportURI></span><br></pre></td></tr></table></figure><p></p><h3 id="为什么?-v4">为什么?</h3><p>上面描述的HSTS头旨在确保所有通过你的网站的连接都进行了加密。然而它并没有指定用什么key。</p><p>网络上的信任是建立在证书授权(CA)模型之上的。你的浏览器和操作系统载体的公共密钥的一些值得信赖的证书颁发机构,通常是专门的公司和/或国家。当一个CA认证机构给你的指定域名颁发一个证书意味着任何一个信任这个CA认证机构的将会自动信任你通过该证书加密的SSL流量。那些CA认证机构负责核实你确实拥有这个域名(可以是任何方面,从发送一封邮件,到要求你提供一个文件,调查你的公司)。</p><p>两个CA认证机构可以向同一个域名颁发证书给两个不同的人,浏览器会信任两者。这就造成了一个问题,特别是因为CA认证机构<a href="https://technet.microsoft.com/library/security/2524375" target="_blank" rel="noopener">可能或者已经</a>被攻破。这允许攻击者MiTM(中间人攻击,<em>Man-in-the-middle</em> attack缩写)任何他们想要的域名,尽管那个域名已经使用了SSL或者HSTS。</p><p>HPKP头试图缓解这种情况。HPKP头让你"固定(pin)"一个证书。当浏览器第一次识别到这个头的时候,它将会保存这个证书。对于每一个到达max-age的请求,浏览器都将会返回失败,除非有一个从服务器发送的证书链带有被固定住的指纹。</p><p>这意味着你可以固定住CA或中间证书的各个部分以免搬起石头砸自己的脚(稍后详述)。</p><p>和上面的HSTS类似,HPKP头也有一些隐私方面的影响。这些已经在<a href="https://tools.ietf.org/html/rfc7469#section-5" target="_blank" rel="noopener">RFC</a>指出了。</p><h3 id="我应该使用吗?-v4">我应该使用吗?</h3><p>也许不要。</p><p>HPKP是一把非常非常锋利的刀。设想一下:如果你固定错了一个证书,或者你丢失了你的钥匙,或者有些地方弄错了,你将会把你的用户锁在你的网站外。所有你能做的就是等待固定过期。</p><p>这篇<a href="https://blog.qualys.com/ssllabs/2016/09/06/is-http-public-key-pinning-dead" target="_blank" rel="noopener">文章</a>列出案例反对了这种观点,并包含一种有趣的方式让攻击者使用HPKP去持有受害人的赌金。</p><p>另一种方法是使用<code>Public-Key-Pins-Report-Only</code>头,这将只报告有些地方出错了而不会将任何一个人锁在门外。这可以至少让你知道你的用户正在被中间攻击人攻击使用假证书。</p><h3 id="如何使用?-v4">如何使用?</h3><p>两种方式</p><ul><li><code>includeSubDomains</code>:HPKP应用到子域名</li><li><code>report-uri</code>:不合法的尝试将会被报告到这里</li></ul><p>你必须为你的要固定的密钥生成一个 base64 编码的指纹,并且你必须使用一个备用密钥。访问<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning#Extracting_the_Base64_encoded_public_key_information" target="_blank" rel="noopener">这篇指南</a>看看如何做。</p><table><thead><tr><th style="text-align:center">平台</th><th style="text-align:center">用法</th></tr></thead><tbody><tr><td style="text-align:center">Rails 4和5</td><td style="text-align:center">使用 <a href="https://github.com/twitter/secureheaders/blob/master/docs/HPKP.md" target="_blank" rel="noopener">secureheaders</a></td></tr><tr><td style="text-align:center">Django</td><td style="text-align:center">编写自定义中间件</td></tr><tr><td style="text-align:center">Express.js</td><td style="text-align:center">使用 <a href="https://helmetjs.github.io/docs/hsts/" target="_blank" rel="noopener">helmet</a></td></tr><tr><td style="text-align:center">Go</td><td style="text-align:center">使用 <a href="https://github.com/unrolled/secure" target="_blank" rel="noopener">unrolled/secure</a></td></tr><tr><td style="text-align:center">Nginx</td><td style="text-align:center"><code>add_header Public-Key-Pins 'pin-sha256="<primary>"; pin-sha256="<backup>"; max-age=5184000; includeSubDomains';</code></td></tr><tr><td style="text-align:center">Apache</td><td style="text-align:center"><code>Header always set Public-Key-Pins 'pin-sha256="<primary>"; pin-sha256="<backup>"; max-age=5184000; includeSubDomains';</code></td></tr></tbody></table><h3 id="我想了解更多-v4">我想了解更多</h3><ul><li><a href="https://tools.ietf.org/html/rfc7469" target="_blank" rel="noopener">RFC 7469</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning" target="_blank" rel="noopener">HTTP Public Key Pinning (HPKP) - MDN</a></li></ul><h2 id="x-frame-options">X-Frame-Options</h2><p></p><figure class="highlight plain"><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">X-Frame-Options: DENY</span><br><span class="line">X-Frame-Options: SAMEORIGIN</span><br><span class="line">X-Frame-Options: ALLOW-FROM https://example.com/</span><br></pre></td></tr></table></figure><p></p><h3 id="为什么?-v5">为什么?</h3><p>Before we started giving dumb names to vulnerabilities, we used to give dumb names to hacking techniques. “Clickjacking” is one of those dumb names.</p><p>The idea goes like this: you create an invisible iframe, place it in focus and route user input into it. As an attacker, you can then trick people into playing a browser-based game while their clicks are being registered by a hidden iframe displaying twitter - forcing them to non-consensually retweet all of your tweets.</p><p>It sounds dumb, but it’s an effective attack.</p><h3 id="我应该使用吗?-v5">我应该使用吗?</h3><p>Yes. Your app is a beautiful snowflake. Do you really want some <a href="https://techcrunch.com/2015/04/08/annotate-this/" target="_blank" rel="noopener">genius</a> shoving it into an iframe so they can vandalize it?</p><h3 id="如何使用?-v5">如何使用?</h3><p><code>X-Frame-Options</code> has three modes, which are pretty self explanatory.</p><ul><li><code>DENY</code> - No one can put this page in an iframe</li><li><code>SAMEORIGIN</code> - The page can only be displayed in an iframe by someone on the same origin.</li><li><code>ALLOW-FROM</code> - Specify a specific url that can put the page in an iframe</li></ul><p>One thing to remember is that you can stack iframes as deep as you want, and in that case, the behavior of <code>SAMEORIGIN</code> and <code>ALLOW-FROM</code> isn’t <a href="https://tools.ietf.org/html/rfc7034#section-2.3.2.2" target="_blank" rel="noopener">specified</a>. That is, if we have a triple-decker iframe sandwich and the innermost iframe has <code>SAMEORIGIN</code>, do we care about the origin of the iframe around it, or the topmost one on the page? ¯_(ツ)_/¯.</p><table><thead><tr><th style="text-align:center">平台</th><th style="text-align:center">用法</th></tr></thead><tbody><tr><td style="text-align:center">Rails 4和5</td><td style="text-align:center"><code>SAMEORIGIN</code>是默认设置的。设置<code>DENY</code>:<code>config.action_dispatch.default_headers['X-Frame-Options'] = "DENY"</code></td></tr><tr><td style="text-align:center">Django</td><td style="text-align:center"><code>MIDDLEWARE = [ ... 'django.middleware.clickjacking.XFrameOptionsMiddleware', ... ]</code>This defaults to <code>SAMORIGIN</code>。设置 <code>DENY</code>: <code>X_FRAME_OPTIONS = 'DENY'</code></td></tr><tr><td style="text-align:center">Express.js</td><td style="text-align:center">使用 <a href="https://helmetjs.github.io/docs/hsts/" target="_blank" rel="noopener">helmet</a></td></tr><tr><td style="text-align:center">Go</td><td style="text-align:center">使用 <a href="https://github.com/unrolled/secure" target="_blank" rel="noopener">unrolled/secure</a></td></tr><tr><td style="text-align:center">Nginx</td><td style="text-align:center"><code>add_header X-Frame-Options "deny";</code></td></tr><tr><td style="text-align:center">Apache</td><td style="text-align:center"><code>Header always set X-Frame-Options "deny"</code></td></tr></tbody></table><h3 id="我想了解更多-v5">我想了解更多</h3><ul><li><a href="https://tools.ietf.org/html/rfc7034" target="_blank" rel="noopener">RFC 7034</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/X-Frame-Options" target="_blank" rel="noopener">X-Frame-Options - MDN</a></li></ul><h2 id="x-content-type-options">X-Content-Type-Options</h2><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">X-Content-Type-Options: nosniff;</span><br></pre></td></tr></table></figure><p></p><h3 id="为什么?-v6">为什么?</h3><p>The problem this header solves is called “MIME sniffing”, which is actually a browser “feature”.</p><p>In theory, every time your server responds to a request it is supposed to set a <code>Content-Type</code> header in order to tell the browser if it’s getting some HTML, a cat gif, or a Flash cartoon from 2008. Unfortunately, the web has always been broken and has never really followed a spec for anything; back in the day lots of people didn’t bother to set the content type header properly.</p><p>As a result, browser vendors decided they should be really helpful and try to infer the content type by inspecting the content itself while completely ignore the content type header. If it looks like a gif, display a gif!, even though the content type is <code>text/html</code>. Likewise, if it looks like we got some HTML, we should render it as such even if the server said it’s a gif.</p><p>This is great, except when you’re running a photo-sharing site, and users can upload photos that look like HTML with javascript included, and suddenly you have a stored XSS attack on your hand.</p><p>The <code>X-Content-Type-Options</code> headers exist to tell the browser to shut up and set the damn content type to what I tell you, thank you.</p><h3 id="我应该使用吗?-v6">我应该使用吗?</h3><p>Yes, just make sure to set your content types correctly.</p><h3 id="如何使用?-v6">如何使用?</h3><table><thead><tr><th>平台</th><th>用法</th></tr></thead><tbody><tr><td>Rails 4 and 5</td><td>默认启用</td></tr><tr><td>Django</td><td><code>SECURE_CONTENT_TYPE_NOSNIFF = True</code></td></tr><tr><td>Express.js</td><td>使用 <a href="https://helmetjs.github.io/docs/dont-sniff-mimetype/" target="_blank" rel="noopener">helmet</a></td></tr><tr><td>Go</td><td>使用 <a href="https://github.com/unrolled/secure" target="_blank" rel="noopener">unrolled/secure</a></td></tr><tr><td>Nginx</td><td><code>add_header X-Content-Type-Options nosniff;</code></td></tr><tr><td>Apache</td><td><code>Header always set X-Content-Type-Options nosniff</code></td></tr></tbody></table><h3 id="我想了解更多-v6">我想了解更多</h3><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options" target="_blank" rel="noopener">X-Content-Type-Options - MDN</a></li></ul><h2 id="referrer-policy">Referrer-Policy</h2><p></p><figure class="highlight plain"><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">Referrer-Policy: "no-referrer" </span><br><span class="line">Referrer-Policy: "no-referrer-when-downgrade" </span><br><span class="line">Referrer-Policy: "origin" </span><br><span class="line">Referrer-Policy: "origin-when-cross-origin"</span><br><span class="line">Referrer-Policy: "same-origin" </span><br><span class="line">Referrer-Policy: "strict-origin" </span><br><span class="line">Referrer-Policy: "strict-origin-when-cross-origin" </span><br><span class="line">Referrer-Policy: "unsafe-url"</span><br></pre></td></tr></table></figure><p></p><h3 id="为什么?-v7">为什么?</h3><p>Ah, the <code>Referer</code> header. Great for analytics, bad for your users’ privacy. At some point the web got woke and decided that maybe it wasn’t a good idea to send it all the time. And while we’re at it, let’s spell “Referrer” correctly<a href="https://blog.appcanary.com/2017/http-security-headers.html#fn4" target="_blank" rel="noopener">4</a>.</p><p>The <code>Referrer-Policy</code> header allows you to specify when the browser will set a <code>Referer</code>header.</p><h3 id="我应该使用吗?-v7">我应该使用吗?</h3><p>t’s up to you, but it’s probably a good idea. If you don’t care about your users’ privacy, think of it as a way to keep your sweet sweet analytics to yourself and out of your competitors’ grubby hands.</p><p>Set <code>Referrer-Policy: "no-referrer"</code></p><h3 id="如何使用?-v7">如何使用?</h3><table><thead><tr><th>平台</th><th>用法</th></tr></thead><tbody><tr><td>Rails 4 and 5</td><td>使用 <a href="https://github.com/twitter/secureheaders" target="_blank" rel="noopener">secureheaders</a></td></tr><tr><td>Django</td><td>编写自定义中间件</td></tr><tr><td>Express.js</td><td>使用 <a href="https://helmetjs.github.io/docs/referrer-policy/" target="_blank" rel="noopener">helmet</a></td></tr><tr><td>Go</td><td>编写自定义中间件</td></tr><tr><td>Nginx</td><td><code>add_header Referrer-Policy "no-referrer";</code></td></tr><tr><td>Apache</td><td><code>Header always set Referrer-Policy "no-referrer"</code></td></tr></tbody></table><h3 id="我想了解更多-v7">我想了解更多</h3><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy" target="_blank" rel="noopener">Referrer Policy - MDN</a></li></ul><h2 id="cookie-options">Cookie Options</h2><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-Cookie: <key>=<value>; Expires=<expiryDate>; Secure; HttpOnly; SameSite=strict</span><br></pre></td></tr></table></figure><p></p><h3 id="为什么?-v8">为什么?</h3><p>This isn’t a security header per se, but there are three different options for cookies that you should be aware of.</p><ul><li>Cookies marked as <code>Secure</code> will only be served over HTTPS. This prevents someone from reading the cookies in a MiTM attack where they can force the browser to visit a given page.</li><li><code>HttpOnly</code> is a misnomer, and has nothing to do with HTTPS (unlike <code>Secure</code> above). Cookies marked as <code>HttpOnly</code> can not be accessed from within javascript. So if there is an XSS flaw, the attacker can’t immediately steal the cookies.</li><li><code>SameSite</code> helps defend against Cross-Origin Request Forgery (CSRF) attacks. This is an attack where a different website the user may be visiting inadvertently tricks them into making a request against your site, i.e. by including an image to make a GET request, or using javascript to submit a form for a POST request. Generally, people defend against this using <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet" target="_blank" rel="noopener">CSRF tokens</a>. A cookie marked as <code>SameSite</code> won’t be sent to a different site.</li></ul><p>It has two modes, lax and strict. Lax mode allows the cookie to be sent in a top-level context for GET requests (i.e. if you clicked a link). Strict doesn’t send any third-party cookies.</p><h3 id="我应该使用吗?-v8">我应该使用吗?</h3><p>You should absolutely set <code>Secure</code> and <code>HttpOnly</code>. Unfortunately, as of writing, SameSite cookies are <a href="http://caniuse.com/#search=samesite" target="_blank" rel="noopener">available</a> only in Chrome and Opera, so you may want to ignore them for now.</p><h3 id="如何使用?-v8">如何使用?</h3><table><thead><tr><th>平台</th><th>用法</th></tr></thead><tbody><tr><td>Rails 4 and 5</td><td>Secure and HttpOnly 默认开启。对于SameSite使用 <a href="https://github.com/twitter/secureheaders" target="_blank" rel="noopener">secureheaders</a></td></tr><tr><td>Django</td><td>Session cookies are HttpOnly 默认开启。 设置安全: <code>SESSION_COOKIE_SECURE = True</code>。SameSite不确定。</td></tr><tr><td>Express.js</td><td><code>cookie: { secure: true, httpOnly: true, sameSite: true }</code></td></tr><tr><td>Go</td><td><code>http.Cookie{Name: "foo", Value: "bar", HttpOnly: true, Secure: true}</code> 对于SameSite, 看这个 <a href="https://github.com/golang/go/issues/15867" target="_blank" rel="noopener">issue</a>.</td></tr><tr><td>Nginx</td><td>你可能不会在Nginx设置会话cookies</td></tr><tr><td>Apache</td><td>你可能不会在Apache设置会话cookies</td></tr></tbody></table><h3 id="我想了解更多-v8">我想了解更多</h3><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies" target="_blank" rel="noopener">Cookies - MDN</a></li></ul>]]></content:encoded>
<comments>https://www.noonme.com/post/2017/01/http-security-headers/#disqus_thread</comments>
</item>
<item>
<title>使用travis-ci自动部署hexo博客</title>
<link>https://www.noonme.com/post/2016/03/travisci-hexo-deploy/</link>
<guid>https://www.noonme.com/post/2016/03/travisci-hexo-deploy/</guid>
<pubDate>Sun, 13 Mar 2016 08:51:37 GMT</pubDate>
<description>
<p>travis-ci是一个持续集成工具,目前已经支持大部分主流语言了,如:node.js、objective-c、android、php、c、java、python、ruby、go等等。travis ci与github集成非常紧密,官方的集成测试托管只支持github项目,而且它对于公有的github仓库免费。在这篇文章中,我将介绍如何通过travis-ci自动化部署hexo博客。</p>
</description>
<content:encoded><![CDATA[<p>travis-ci是一个持续集成工具,目前已经支持大部分主流语言了,如:node.js、objective-c、android、php、c、java、python、ruby、go等等。travis ci与github集成非常紧密,官方的集成测试托管只支持github项目,而且它对于公有的github仓库免费。在这篇文章中,我将介绍如何通过travis-ci自动化部署hexo博客。</p><a id="more"></a><h2 id="开启travis-ci">开启travis-ci</h2><p>首先去<a href="https://travis-ci.org" target="_blank" rel="noopener">travis-ci</a>官网,点击右上角<code>Sign in with GitHub</code>通过github授权登录。然后去到个人信息页面,开启需要使用travis的项目关:</p><p><img src="http://77g5i3.com1.z0.glb.clouddn.com//uploads/travis-ci-start.png" alt="travis-ci"></p><h2 id="加密私钥">加密私钥</h2><p>首先我们要安装travis-ci的命令行工具</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gem install travis</span><br></pre></td></tr></table></figure><p></p><p>然后通过命令行登录travis-ci:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># https://github.com/travis-ci/travis.rb#login</span></span><br><span class="line">travis login --auto</span><br></pre></td></tr></table></figure><p></p><p>登录需要输入github用户名和密码。如果不想通过用户名登录的话,也可以通过github-token登录(反正我是通过这种方式登录的,通过输入用户名密码登录总是报<code>Validation Failed</code>的错误):</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">travis login --github-token <span class="string">'token'</span></span><br></pre></td></tr></table></figure><p></p><p>github-token可以去<a href="https://github.com/settings/tokens" target="_blank" rel="noopener">https://github.com/settings/tokens</a>查看,如果<code>Personal access tokens</code>列表里面有的话可以选中一个点击<code>Edit</code>,然后点击<code>Regenerate token</code>重新生成就可以看到token了。没有的话点击<code>Generate new token</code>生成一个token。</p><p><img src="http://77g5i3.com1.z0.glb.clouddn.com/github-token.png" alt="github-token"></p><p>可以检查一下是否登录成功:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">travis whoami</span><br></pre></td></tr></table></figure><p></p><p>接着我们需要对ssh私钥<code>id_rsa</code>进行加密:</p><p></p><figure class="highlight plain"><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"># github支持对每个项目设置Deploy keys,并且赋予读取权限</span><br><span class="line"># 所以ssh私钥最好是单独为当前项目准备的,不然travis就对整个账户有了控制权限</span><br><span class="line"># 你可以通过ssh-keygen命令生成一个新的ssh密钥对</span><br><span class="line"># 然后添加到项目的Settings->Deploy keys里面,并注意勾上Allow write access</span><br><span class="line"></span><br><span class="line">travis encrypt-file ~/.ssh/id_rsa --add</span><br></pre></td></tr></table></figure><p></p><p>其中<code>--add</code>参数,travis的客户端会自动检测当前目录中的git信息,并且把生成的解密key添加到<code>.travis.yml</code>中去。在进行此步操作前,目录下要先存在<code>.travis.yml</code>文件,否则会报错。</p><p>关于<code>encrypt-file</code>详细用法可以查看travis api文档<a href="https://docs.travis-ci.com/user/encrypting-files/" target="_blank" rel="noopener">encrypting-files</a></p><p>执行完上面的命令之后,会在当前目录生成一个<code>id_rsa.enc</code>的加密文件,我们可以在当前项目新建一个<code>.travis</code>的目录,然后把该文件放到此目录一起添加至git仓库。</p><p>由于git在第一次连接的时候会询问<code>Are you sure you want to continue connecting (yes/no)?</code>,因此我们需要配置一下ssh让它不询问。在项目的<code>.travis</code>目录下面新建<code>ssh_config</code>文件,下面是我的配置,可以根据你的需要修改:</p><p></p><figure class="highlight plain"><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">Host github.com</span><br><span class="line"> User git</span><br><span class="line"> StrictHostKeyChecking no</span><br><span class="line"> IdentityFile ~/.ssh/id_rsa</span><br><span class="line"> IdentitiesOnly yes</span><br><span class="line"></span><br><span class="line">Host gitcafe.com</span><br><span class="line"> User git</span><br><span class="line"> StrictHostKeyChecking no</span><br><span class="line"> IdentityFile ~/.ssh/id_rsa</span><br><span class="line"> IdentitiesOnly yes</span><br><span class="line"></span><br><span class="line">Host git.coding.net</span><br><span class="line"> User git</span><br><span class="line"> StrictHostKeyChecking no</span><br><span class="line"> IdentityFile ~/.ssh/id_rsa</span><br><span class="line"> IdentitiesOnly yes</span><br></pre></td></tr></table></figure><p></p><p>到此构建准备工作完毕。</p><h2 id="构建配置">构建配置</h2><p>上面的操作就是为了让travis-ci对仓库拥有push权限,但是又不能把我们的私钥暴露出去。</p><p>下面就是我的<code>.travis.yml</code>文件的配置信息:</p><p></p><figure class="highlight yml"><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="attr">language:</span> <span class="string">node_js</span></span><br><span class="line"></span><br><span class="line"><span class="attr">sudo:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># master为hexo博客所在分支</span></span><br><span class="line"><span class="attr">branches:</span></span><br><span class="line"><span class="attr"> only:</span></span><br><span class="line"><span class="bullet"> -</span> <span class="string">master</span></span><br><span class="line"></span><br><span class="line"><span class="attr">before_install:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">openssl</span> <span class="string">aes-256-cbc</span> <span class="bullet">-K</span> <span class="string">$encrypted_0a6446eb3ae3_key</span> <span class="bullet">-iv</span> <span class="string">$encrypted_0a6446eb3ae3_key</span> <span class="bullet">-in</span> <span class="string">.travis/id_rsa.enc</span> <span class="bullet">-out</span> <span class="string">~/.ssh/id_rsa</span> <span class="bullet">-d</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">chmod</span> <span class="number">600</span> <span class="string">~/.ssh/id_rsa</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">eval</span> <span class="string">$(ssh-agent)</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">ssh-add</span> <span class="string">~/.ssh/id_rsa</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">cp</span> <span class="string">.travis/ssh_config</span> <span class="string">~/.ssh/config</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">git</span> <span class="string">config</span> <span class="bullet">--global</span> <span class="string">user.name</span> <span class="string">'bukas'</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">git</span> <span class="string">config</span> <span class="bullet">--global</span> <span class="string">user.email</span> <span class="string">'bukas@gmail.com'</span></span><br><span class="line"></span><br><span class="line"><span class="attr">install:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">npm</span> <span class="string">install</span> <span class="string">hexo-cli</span> <span class="bullet">-g</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">npm</span> <span class="string">install</span></span><br><span class="line"></span><br><span class="line"><span class="attr">script:</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">hexo</span> <span class="string">clean</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">hexo</span> <span class="string">g</span></span><br><span class="line"><span class="bullet">-</span> <span class="string">hexo</span> <span class="string">d</span></span><br></pre></td></tr></table></figure><p></p><p>其中的<code>$encrypted_0a6446eb3ae3_key</code>便是你<code>travis encrypt-file</code>的时候生成的。如果你在执行<code>encrypt-file</code>的时候存在<code>.travis.yml</code>文件,它将会自动把<code>openssl ...</code>这段添加进去。</p><p>有关<code>.travis.yml</code>文件的更多配置可以看这里:<a href="https://docs.travis-ci.com/user/customizing-the-build/" target="_blank" rel="noopener">customizing-the-build</a></p><h2 id="结语">结语</h2><p>把你的项目修改提交一下,你将在travi-ci看到一条build信息。</p><p>如果你也想在github的README.md显示构建成功与否的标示,你可以这样:</p><p></p><figure class="highlight md"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">build-info</span>](<span class="link">https://travis-ci.org/userName/repoName.svg</span>)</span><br></pre></td></tr></table></figure><p></p>]]></content:encoded>
<comments>https://www.noonme.com/post/2016/03/travisci-hexo-deploy/#disqus_thread</comments>
</item>
<item>
<title>AngularJS动态加载Controller</title>
<link>https://www.noonme.com/post/2016/03/angularjs-dynamic-load-controller/</link>
<guid>https://www.noonme.com/post/2016/03/angularjs-dynamic-load-controller/</guid>
<pubDate>Thu, 03 Mar 2016 16:19:47 GMT</pubDate>
<description>
<p>AngularJS原生并不支持动态加载Controller的方法,但是却提供注册Controller的方法。接下来就来看下如何实现动态加载Controller。</p>
</description>
<content:encoded><![CDATA[<p>AngularJS原生并不支持动态加载Controller的方法,但是却提供注册Controller的方法。接下来就来看下如何实现动态加载Controller。</p><a id="more"></a><p>我们把实现动态加载Controller方法封装到一个通用的模块里面,并命名这个模块为<code>ngCommon</code>。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params">angular</span>) </span>{<span class="string">'use strict'</span>;</span><br><span class="line"> <span class="keyword">var</span> CommonApp = angular.module(<span class="string">'ngCommon'</span>);</span><br><span class="line"> ...</span><br><span class="line">})(angular);</span><br></pre></td></tr></table></figure><p></p><p>接下来我们实现一个动态加载js的方法<code>$require</code>。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 记录已加载的js */</span></span><br><span class="line"><span class="keyword">var</span> loaded = {};</span><br><span class="line"><span class="comment">/* 检测是否加载 */</span></span><br><span class="line"><span class="keyword">var</span> checkLoaded = <span class="function"><span class="keyword">function</span> (<span class="params">url</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> !url || !angular.isString(url) || loaded[url];</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">CommonApp.factory(<span class="string">'$require'</span>, [<span class="string">'$document'</span>, <span class="string">'$q'</span>, <span class="string">'$rootScope'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">$document, $q, $rootScope</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">url</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> script = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">var</span> onload = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">var</span> doc = $<span class="built_in">document</span>[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">var</span> body = doc.body;</span><br><span class="line"> <span class="keyword">var</span> deferred = $q.defer();</span><br><span class="line"> <span class="keyword">if</span> (checkLoaded(url)) {</span><br><span class="line"> deferred.resolve();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> script = doc.createElement(<span class="string">'script'</span>);</span><br><span class="line"> onload = <span class="function"><span class="keyword">function</span> (<span class="params">info</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (info === <span class="number">1</span>) {</span><br><span class="line"> deferred.reject();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> loaded[url] = <span class="number">1</span>;</span><br><span class="line"> <span class="comment">/* AngularJS < 1.2.x 请使用$timeout */</span></span><br><span class="line"> $rootScope.$evalAsync(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> deferred.resolve();</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> script.onload = script.onerror = <span class="literal">null</span>;</span><br><span class="line"> body.removeChild(script);</span><br><span class="line"> script = <span class="literal">null</span>;</span><br><span class="line"> };</span><br><span class="line"> script.onload = onload;</span><br><span class="line"> script.onerror = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> onload(<span class="number">1</span>);</span><br><span class="line"> };</span><br><span class="line"> script.async = <span class="literal">true</span>;</span><br><span class="line"> script.src = url;</span><br><span class="line"> body.appendChild(script);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> deferred.promise;</span><br><span class="line"> };</span><br><span class="line">}]);</span><br></pre></td></tr></table></figure><p></p><p>然后重点来了,通过<code>$routeProvider route</code>的<code>resolve</code>功能来实现动态加载Controller。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">CommonApp.provider(<span class="string">'$routeResolver'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.$<span class="keyword">get</span> = function () {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">this</span>.route = <span class="function"><span class="keyword">function</span> (<span class="params">routeCnf</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> controller = routeCnf.controller;</span><br><span class="line"> <span class="keyword">var</span> controllerUrl = routeCnf.controllerUrl;</span><br><span class="line"> <span class="keyword">if</span> (controllerUrl) {</span><br><span class="line"> routeCnf.reloadOnSearch = routeCnf.reloadOnSearch || <span class="literal">false</span>;</span><br><span class="line"> routeCnf.resolve = {</span><br><span class="line"> load: [<span class="string">'$route'</span>, <span class="string">'$require'</span>, <span class="string">'ControllerChecker'</span>,</span><br><span class="line"> <span class="function"><span class="keyword">function</span> (<span class="params">$route, $require, ControllerChecker</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> controllerName = angular.isFunction(controller) ? controller($route.current.params) : controller;</span><br><span class="line"> <span class="keyword">var</span> url = angular.isFunction(controllerUrl) ? controllerUrl($route.current.params) : controllerUrl;</span><br><span class="line"> <span class="keyword">if</span> (checkLoaded(url) || (controllerName && ControllerChecker.exists(controllerName))) {</span><br><span class="line"> loaded[url] = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> $<span class="built_in">require</span>(url);</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> routeCnf;</span><br><span class="line"> };</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p></p><p>看上面的代码中还注入了一个叫<code>ControllerChecker</code>的,这个是用来检测当前Controller是否已经注册了,如果未注册,那么我们就加载相关js注册新的Controller。代码如下:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">CommonApp.service(<span class="string">'ControllerChecker'</span>, [<span class="string">'$controller'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">$controller</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> exists: <span class="function"><span class="keyword">function</span> (<span class="params">controllerName</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (angular.isFunction(<span class="built_in">window</span>[controllerName])) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> $controller(controllerName, {}, <span class="literal">true</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">}]);</span><br></pre></td></tr></table></figure><p></p><p>最后我们来添加一个注动态册的方法。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">CommonApp.setupRegister = <span class="function"><span class="keyword">function</span> (<span class="params">module</span>) </span>{</span><br><span class="line"> <span class="built_in">module</span>.config([</span><br><span class="line"> <span class="string">'$controllerProvider'</span>,</span><br><span class="line"> <span class="string">'$compileProvider'</span>,</span><br><span class="line"> <span class="string">'$filterProvider'</span>,</span><br><span class="line"> <span class="string">'$provide'</span>,</span><br><span class="line"> <span class="function"><span class="keyword">function</span> (<span class="params">$controllerProvider, $compileProvider, $filterProvider, $provide</span>) </span>{</span><br><span class="line"> <span class="built_in">module</span>.register = {</span><br><span class="line"> controller: $controllerProvider.register,</span><br><span class="line"> directive: $compileProvider.directive,</span><br><span class="line"> filter: $filterProvider.register,</span><br><span class="line"> factory: $provide.factory,</span><br><span class="line"> service: $provide.service,</span><br><span class="line"> value: $provide.value,</span><br><span class="line"> constant: $provide.constant</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> ]);</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p></p><p>到此已经基本完成了,如何使用呢?</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> DemoApp = angular.module(<span class="string">'DemoApp'</span>,[<span class="string">'ngRoute'</span>,<span class="string">'ngCommon'</span>]);</span><br><span class="line"><span class="comment">/* 调用动态注册方法,为当前模块添加动态注册方法 */</span></span><br><span class="line">angular.module(<span class="string">'ngCommon'</span>).setupRegister(DemoApp);</span><br><span class="line">DemoApp.config([<span class="string">'$routeProvider'</span>, <span class="string">'$routeResolverProvider'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">$routeProvider, $routeResolverProvider</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> route = $routeResolverProvider.route;</span><br><span class="line"> $routeProvider.when(<span class="string">'/index'</span>, route({</span><br><span class="line"> templateUrl: <span class="string">'./view/index.html'</span>),</span><br><span class="line"> controller: <span class="string">'IndexController'</span>, <span class="comment">/* 在此申明了controller就不需要再html里面申明ng-controller了 */</span></span><br><span class="line"> controllerUrl: <span class="string">'./controller/index.js'</span>)</span><br><span class="line"> }))</span><br><span class="line"> .otherwise(<span class="string">'/index'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* ./controller/index.js */</span></span><br><span class="line">DemoApp.register.controller(<span class="string">'IndexController'</span>, [<span class="string">'$scope'</span>, <span class="string">'$require'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">$scope, $require</span>) </span>{</span><br><span class="line"> ...</span><br><span class="line"> <span class="comment">/* 动态加载某个js文件 */</span></span><br><span class="line"> $<span class="built_in">require</span>(url).then(<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></pre></td></tr></table></figure><p></p>]]></content:encoded>
<comments>https://www.noonme.com/post/2016/03/angularjs-dynamic-load-controller/#disqus_thread</comments>
</item>
<item>
<title>AngularJS $apply vs $timeout vs $digest vs $evalAsync</title>
<link>https://www.noonme.com/post/2016/02/angularjs-$apply-$digest-$timeout-$evalAsync/</link>
<guid>https://www.noonme.com/post/2016/02/angularjs-$apply-$digest-$timeout-$evalAsync/</guid>
<pubDate>Thu, 04 Feb 2016 13:06:26 GMT</pubDate>
<description>
<p>AngularJS提供很多自带的方法,如:<code>$apply</code>, <code>$timeout</code>, <code>$digest</code> 和 <code>$evalAsync</code>。这四个方法虽然很常见,但对于他们的差别以及如何正确使用有时候我们会感到有些困惑,因此我决定深入分析一下它们。</p>
</description>
<content:encoded><![CDATA[<p>AngularJS提供很多自带的方法,如:<code>$apply</code>, <code>$timeout</code>, <code>$digest</code> 和 <code>$evalAsync</code>。这四个方法虽然很常见,但对于他们的差别以及如何正确使用有时候我们会感到有些困惑,因此我决定深入分析一下它们。</p><a id="more"></a><h2 id="apply">$apply</h2><p>这个核心方法可以让你显式启动<code>$digest</code>循环。这意味着所有的watcher将会被检测;整个应用启动$digest循环。在内部会执行一个可选的方法之后,会调用<code>$rootScope.$digest()</code>;。<code>$apply()</code>对机器来说是一个困难的处理过程,在绑定过多的时候可能会引发性能问题。</p><p><code>$apply()</code>方法有两种调用形式。第一种会接受一个function作为参数,执行该function并且触发一轮<code>$digest</code>循环。第二种会不接受任何参数,只是触发一轮<code>$digest</code>循环。需要记住的是你总是应该使用接受一个function作为参数的<code>$apply()</code>方法。这是因为当你传入一个function到<code>$apply()</code>中的时候,这个function会被包装到一个<code>try…catch</code>块中,所以一旦有异常发生,该异常会被<code>$exceptionHandler</code> service处理。</p><p>那我们在什么情况下应该使用<code>$apply()</code>方法呢?</p><p>下面的例子中,第一段代码并不会更新ui上绑定的<code>message</code>变量,因为<code>setTimeout</code>并不是AngularJS原生的方法。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyCtrl</span>(<span class="params">$scope</span>) </span>{</span><br><span class="line"> $scope.message = <span class="string">'Hi'</span>;</span><br><span class="line"> setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> $scope.message = <span class="string">'Hello!'</span>;</span><br><span class="line"> <span class="comment">// AngularJS unaware of update to $scope</span></span><br><span class="line"> }, <span class="number">2000</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyCtrl</span>(<span class="params">$scope</span>) </span>{</span><br><span class="line"> $scope.message = <span class="string">'Hello'</span>;</span><br><span class="line"> setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> $scope.$apply(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> $scope.message = <span class="string">'Hi'</span>;</span><br><span class="line"> });</span><br><span class="line"> }, <span class="number">2000</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><p>在AngularJS中几乎你的所有代码都会包在$scope.$apply()中,Events像是ng-click, controller initialization, $http callbacks全部都是被<code>$scope.$apply()</code>给包住的。所以你不需要自己去调用<code>$scope.$apply()</code>。而且在<code>$scope.$apply()</code>中调用<code>$scope.$apply()</code>会出现错误。</p><p>所以真正会用到<code>$scope.$apply()</code>的情況是浏览器DOM events, setTimeout, XHR(XMLHttpREquest)或是第三方组件。</p><p>如果我们在应用中频繁调用<code>$apply</code>,可能会出现<code>$digest already in progress</code>的错误。这是因为一次<code>$digest</code>循环可能需要一段时间。我们可以通过<code>$timeout</code>或<code>$evalAsync</code>来解决这个问题。</p><h2 id="digest">$digest</h2><p>负责检查models和views之间的改变,然后同步更新UI和Model。</p><p>当一个<code>$digest</code>循环运行时,watchers会被执行来检查scope中的models是否发生了变化。如果发生了变化,那么相应的listener函数就会被执行。这涉及到一个重要的问题。如果listener函数本身会修改一个scope model呢?AngularJS会怎么处理这种情况?</p><p>答案是<code>$digest</code>循环不会只运行一次。在当前的一次循环结束后,它会再执行一次循环用来检查是否有models发生了变化。这就是脏检查(Dirty Checking),它用来处理在listener函数被执行时可能引起的model变化。因此,<code>$digest</code>循环会持续运行直到model不再发生变化,或者<code>$digest</code>循环的次数达到了10次。因此,尽可能地不要在listener函数中修改model。</p><p><code>$digest</code>循环最少也会运行两次,即使在listener函数中并没有改变任何model,它也会多运行一次来确保models没有变化。</p><p>当我们直接调用<code>$digest</code>方法时,它会在当前作用域和它的子项启动<code>$digest</code>循环。你需要注意他的父作用域将不会被检测也不会被影响。</p><p>所以如果你只需要更新当前的作用域或者它的子项的话,使用$digest,而且要防止在整个应用里运行新的<code>$digest</code>循环。这在性能上的好处是显而易见的。</p><h2 id="timeout">$timeout</h2><p>如果你正在使用AngularJS 1.2.X之前的版本的话,那么<code>$timeout()</code>是用来处理Angular环境之外的代码更新scope绑定最简单有效的方法。</p><p>它是对<code>window.setTimeout</code>的包装,用来延迟执行一个函数。当我们在Controller中更改了数据模型时,此时DOM还没有得到更新(<code>$digest</code>循环还没开始)。如果我们希望DOM刷新后执行某些操作,就可以使用<code>$timeout</code>。</p><p>而且当<code>$timeout</code>异步完成后,AngularJS会自动触发<code>$apply</code>,当然你也可以在调用<code>$timeout</code>方法的时候传入第三个参数<code>false</code>不执行<code>$apply()</code></p><p>调用<code>$timeout</code>并不会出现<code>$digest already in progress</code>的错误因为它会告知AngularJS当前<code>$digest</code>循环执行完毕之后有一个timeout在等待,以此确保<code>$digest</code>循环不会发生碰撞,因此<code>$timeout</code>将会开启一次新的<code>$digest</code>循环。</p><h2 id="evalasync">$evalAsync</h2><p><code>$evalAsync()</code>方法是AngularJS 1.2.X之后加入的一个新方法,在我看来它就是更加灵活的<code>$timeout()</code>。调用<code>$evalAsync()</code>方法将在当前循环或下一个循环执行表达式。</p><p>假设你调用了多次<code>$evalAsync</code>,然后任一<code>$digest</code>循环都可能在同一时间执行。来自<code>$evalAsync</code>的表达式将会被加入到一个队列中去,这些表达式都将会是当前<code>$digest</code>循环的一部分,因此并不会有新的<code>$digest</code>循环执行。这就是为何<code>$evalAsync</code>更加高效,它把之前可能创建多次<code>$digest</code>循环的操作降低到了一次<code>$digest</code>循环处理。如果当前并没有<code>$digest</code>循环在执行的话,它就相当于<code>$timeout</code>,会创建一个默认<code>10ms</code>后执行的回调函数,<code>10ms</code>之后便会执行<code>$rootScope.$digest()</code>。</p><p>因此如果你正使用大于AngularJS 1.2.X版本,使用$evalAsync,这将提高你的应用的性能。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2016/02/angularjs-$apply-$digest-$timeout-$evalAsync/#disqus_thread</comments>
</item>
<item>
<title>iTerm2使用rz、sz远程上传或下载文件</title>
<link>https://www.noonme.com/post/2016/02/mac-iterm2-rz-sz/</link>
<guid>https://www.noonme.com/post/2016/02/mac-iterm2-rz-sz/</guid>
<pubDate>Wed, 03 Feb 2016 14:48:17 GMT</pubDate>
<description>
<p>在日常开发中,我们经常需要上传文件到服务器或者从服务器下载文件。在Windows下SecureCRT为我们提供了很方便的上传下载工具sz与rz,但是mac下一般都是通过scp命令来完成的,虽然也很方便,但是有些场景下是不能使用的,比如目前公司登录服务器需要经过跳板机。本篇我们就介绍一下如何在mac下使用rz、sz上传下载文件。</p>
</description>
<content:encoded><![CDATA[<p>在日常开发中,我们经常需要上传文件到服务器或者从服务器下载文件。在Windows下SecureCRT为我们提供了很方便的上传下载工具sz与rz,但是mac下一般都是通过scp命令来完成的,虽然也很方便,但是有些场景下是不能使用的,比如目前公司登录服务器需要经过跳板机。本篇我们就介绍一下如何在mac下使用rz、sz上传下载文件。</p><a id="more"></a><h2 id="scp">scp</h2><p>先介绍下如何使用scp命令。</p><p><strong>上传</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp -r local_folder remote_username@remote_ip:remote_folder</span><br></pre></td></tr></table></figure><p></p><p>或者</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp -r local_folder remote_ip:remote_folder</span><br></pre></td></tr></table></figure><p></p><p><strong>下载</strong></p><p>下载和上传对应,只需要修改后两个参数顺序即可,即调整源文件和目标文件顺序。</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp -r remote_username@remote_ip:remote_folder local_folder</span><br></pre></td></tr></table></figure><p></p><blockquote><p>几个可能用到的参数:</p><p>-v 和大多数linux命令中的-v意思一样,用来显示进度。可以用来查看连接、认证、或是配置错误。</p><p>-r 递归处理,将指定目录下的文档和子目录一并处理</p><p>-C 使能压缩选项</p><p>-P 选择端口。注意-p已经被rcp使用</p><p>-4 强行使用IPV4地址</p><p>-6 强行使用IPV6地址</p></blockquote><h2 id="配置rz-sz">配置rz、sz</h2><p>在此之前你必须要安装iTerm2,然后:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install lrzsz</span><br></pre></td></tr></table></figure><p></p><p>下载iterm2-zmodem:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /usr/<span class="built_in">local</span>/bin</span><br><span class="line">sudo wget https://raw.github.com/mmastrac/iterm2-zmodem/master/iterm2-send-zmodem.sh</span><br><span class="line">sudo wget https://raw.github.com/mmastrac/iterm2-zmodem/master/iterm2-recv-zmodem.sh</span><br><span class="line">sudo chmod 777 /usr/<span class="built_in">local</span>/bin/iterm2-*</span><br></pre></td></tr></table></figure><p></p><p>打开Item2,点击<code>preferences → profiles</code>,选择某个<code>profile</code>,如Default,之后继续选择<code>advanced → triggers</code>,添加编辑添加如下triggers:</p><table><thead><tr><th>Regular Expression</th><th>Action</th><th>Parameters</th></tr></thead><tbody><tr><td>rz waiting to receive.\*\*B0100</td><td>Run Silent Coprocess</td><td>/usr/local/bin/iterm2-send-zmodem.sh</td></tr><tr><td>\*\*B00000000000000</td><td>Run Silent Coprocess</td><td>/usr/local/bin/iterm2-recv-zmodem.sh</td></tr></tbody></table><p>也可以去这里查看:<a href="https://github.com/mmastrac/iterm2-zmodem" target="_blank" rel="noopener">https://github.com/mmastrac/iterm2-zmodem</a></p>]]></content:encoded>
<comments>https://www.noonme.com/post/2016/02/mac-iterm2-rz-sz/#disqus_thread</comments>
</item>
<item>
<title>那些常用的svn和git命令</title>
<link>https://www.noonme.com/post/2015/09/those-common-svn-and-git-commands/</link>
<guid>https://www.noonme.com/post/2015/09/those-common-svn-and-git-commands/</guid>
<pubDate>Thu, 03 Sep 2015 14:51:37 GMT</pubDate>
<description>
<p>版本管理基本上是多人协作开发中必不可少的工具,常用的版本管理工具有:svn和git。虽然都有可视化的工具帮助我们使用这些工具,然而当你用上命令行之后,我想你会选择抛弃这些可视化工具。下面是我整理的一些常用的svn和git命令。</p>
</description>
<content:encoded><![CDATA[<p>版本管理基本上是多人协作开发中必不可少的工具,常用的版本管理工具有:svn和git。虽然都有可视化的工具帮助我们使用这些工具,然而当你用上命令行之后,我想你会选择抛弃这些可视化工具。下面是我整理的一些常用的svn和git命令。</p><a id="more"></a><p><img src="http://blog.u.qiniudn.com/uploads/git&svn.png" alt="git&amp;svn"></p><h2 id="svn篇">Svn篇</h2><p>首先你可以通过<code>svn help/h</code>来查看帮助信息。</p><h3 id="开始工作">开始工作</h3><p><strong>检出(<code>checkout</code>)服务器数据到本地</strong></p><p>你刚入职一家公司,或新加入某个团队,立马参与到一个项目中,那么就得获取项目代码,开始你的项目生涯。这个时候一般你需要检出项目代码:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">svn checkout/co [directory] project(本地目录名,可选)</span><br><span class="line">// 检出版本3</span><br><span class="line">svn checkout/co –revision/r 3 [directory] project(本地目录名,可选)</span><br></pre></td></tr></table></figure><p></p><p>接着你就可以通过<code>svn info</code>来查看版本信息了。</p><p><strong>导入(<code>import</code>)项目</strong></p><p>有时候项目尚未创建,你需要将本地的目录放到SVN版本仓库中:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn import project(本地目录名) [directory]</span><br></pre></td></tr></table></figure><p></p><p>然后可以通过<code>svn list/ls</code>确认已经在版本仓库中了。</p><h3 id="更新">更新</h3><p>每次你开始编码前,你都最好先更新下本地的工作目录:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> project</span><br><span class="line">svn update/up</span><br><span class="line">// 更新到版本3</span><br><span class="line">svn update/up –revision/r 3</span><br></pre></td></tr></table></figure><p></p><p>这样你就可以在新的项目代码基础上工作了。</p><h3 id="修改">修改</h3><p>可能你写了一个新的模块,增加了一些新的文件,需要纳入项目的版本控制:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn add index.html list.html ...</span><br></pre></td></tr></table></figure><p></p><p>可能你发现某个模块已经陈旧了,不再使用了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn delete/del/remove/rm hello.html</span><br></pre></td></tr></table></figure><p></p><p>可能你发现一个模块的命名不太合理,需要改名:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn move/mv main.css common.css</span><br></pre></td></tr></table></figure><p></p><p>可能你要创建一个新的较大的模块,需要新增目录:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn mkdir list</span><br></pre></td></tr></table></figure><p></p><p>可能你发现要写的模块代码似于旧的模块,直接复制整个代码:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn copy/cp users/list.js list/list.js</span><br></pre></td></tr></table></figure><p></p><h3 id="检查">检查</h3><p>忙碌的一天过去了,或者一个任务完成了,这个时候一般会将你的工作成果,也就是代码更新到版本仓库。</p><p>习惯上会先检查下修改状态:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn status/<span class="built_in">stat</span>/st</span><br></pre></td></tr></table></figure><p></p><p>看到一些SVN状态位信息,确认是修改了哪些文件,之后一般会自己code review一下代码的改动,可能有的人会习惯直接用SVN方式来查看:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">svn diff/di folder(本地目录名,可选,默认当前目录)</span><br><span class="line">// 查看index.html当前版本和版本3的差别</span><br><span class="line">svn diff/di –revision/r 3 index.html</span><br><span class="line">// 查看index.html版本3和版本4的差别</span><br><span class="line">svn diff/di –revision/r 3:4 index.html</span><br></pre></td></tr></table></figure><p></p><p>一般来说这个时候,没有什么特殊情况,就直接进入“提交”阶段了,然后结束一个工作日或工作周期,但难免会有些特殊情况出现。</p><h3 id="取消修改">取消修改</h3><p>当你code review完后,发现有些改动不满意,你可能又会取消这些修改:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">svn revert index.html</span><br><span class="line">// 回滚整个目录</span><br><span class="line">svn revert . -R/--recursive</span><br></pre></td></tr></table></figure><p></p><h3 id="分支操作">分支操作</h3><h4 id="创建分支">创建分支</h4><p><strong>创建一个分支</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn copy/cp svn://xxx.com/repo/trunk svn://xxx.com/repo/branches/<span class="built_in">test</span> -m <span class="string">'make branch test'</span></span><br></pre></td></tr></table></figure><p></p><p><strong>把工作目录转到分支</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn switch/sw svn://xxx.com/repo/branches/<span class="built_in">test</span></span><br></pre></td></tr></table></figure><p></p><p>当然,也可以再转到主干<code>svn switch/sw svn://xxx.com/repo/trunk</code>。</p><h4 id="给分支打标签">给分支打标签</h4><p>复制最新的发布分支为标签:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn copy/cp svn://xxx.com/repo/branches/<span class="built_in">test</span> svn://xxx.com/repo/tags/test_tag</span><br></pre></td></tr></table></figure><p></p><h4 id="合并一个分支到主干">合并一个分支到主干</h4><p><strong>查找到分支版本</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> branches/<span class="built_in">test</span>(分支目录)</span><br><span class="line">svn <span class="built_in">log</span> –stop-on-copy</span><br></pre></td></tr></table></figure><p></p><p>最后一个r11340就是创建分支时的reversion,也可:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> trunk(主干目录)</span><br><span class="line">svn -q –stop-on-copy svn://xxx.com/repo/branches/<span class="built_in">test</span>(分支url)</span><br></pre></td></tr></table></figure><p></p><p>这条命令会查询出自创建分支以后分支上的所有修改,最下面的那个版本号就是我们要找的版本号。</p><p><strong>合并到主干</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> trunk(主干目录)</span><br><span class="line">svn merge -r 11340(分支版本):HEAD svn://xxx.com/repo/branches/<span class="built_in">test</span>(分支url)</span><br></pre></td></tr></table></figure><p></p><h4 id="两个分支合并">两个分支合并</h4><p>假设99是从旧主干引出,100打完tag,表示是新主干。</p><p>合并最新代码的意思是:将新主干与旧主干比对,并添加到99中。这样99既有自己的新增的代码,也同时有最新线上的代码。</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> 99_Branch</span><br><span class="line">svn merge svn://xxx.com/repo/tags/project_Old_BL svn://xxx.com/repo/tags/project_New_BL</span><br><span class="line">svn ci -m <span class="string">'merge 100 trunk'</span></span><br></pre></td></tr></table></figure><p></p><p>但是后来,其他人又向100提了代码,所以还需要将100分支(即打了tag后的100,打了tag前的100已是主干)合并至99中。</p><p>合并办法:找出100分支,比对与新主干之间的差别,并添加到99中。这样99就有最新的全部代码了。</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> 99_Branch</span><br><span class="line">svn merge svn://xxx.com/repo/tags/project_New_BL svn:/xxx.com/repo/branches/100_Branch</span><br><span class="line">svn ci -m <span class="string">'merge 100 branch'</span></span><br></pre></td></tr></table></figure><p></p><h4 id="发布">发布</h4><p>给当前主干打个标签,并且这个标签不再改动了,但是实际上标签和分支是一个意思,你可以在标签上继续做改动,但这不推荐。</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn copy/cp svn://xxx.com/repo/trunk svn://xxx.com/repo/tags/RB-1.0</span><br></pre></td></tr></table></figure><p></p><h4 id="合并主干到分支">合并主干到分支</h4><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn merge -r LastRevisionMergedFromTrunkToBranch:HEAD svn:/xxx.com/repo/branches/99_Branch</span><br></pre></td></tr></table></figure><p></p><h3 id="解决冲突">解决冲突</h3><p>当发生冲突的时候,会提示如下信息:</p><blockquote><p>Conflict discovered in 'index.html'.</p><p>Select: (p) postpone, (df) diff-full, (e) edit,</p><p>(mc) mine-conflict, (tc) theirs-conflict,</p><p>(s) show all options:</p><p>svn detects that theres a conflict here and require you to take some kind of action.</p></blockquote><p>如果你输入s选项,则会列出所有svn解决冲突的选项,如下所示:</p><blockquote><p>(e) edit - change merged file in an editor #直接进入编辑</p><p>(df) diff-full -show all changes made to merged file #显示更改至目标文件的所有变化</p><p>(r) resolved -accept merged version of file</p><p>(dc) display-conflict -show all conflicts(ignoring merged version) #显示所有冲突</p><p>(mc) mine-conflict -accept my version for all conflicts (same) #冲突以本地为准</p><p>(tc) theirs-conflict -accept their version for all conflicts (same) #冲突以服务器为准</p><p>(mf) mine-full -accept my version of entire file (even non-conflicts) #完全以本地为准</p><p>(tf) theirs-full -accept their version of entire file (same) #完全以服务器为准</p><p>(p) postpone -mark the conflict to be resolved later #标记冲突,稍后解决</p><p>(l) launch -launch external tool to resolve conflict</p><p>(s) show all -show this list</p></blockquote><p>一般我们会选择<code>p</code>稍后解决冲突,这样会生成三个文件:.mine, .rOLDREV, .rNEWREV。比如:</p><p></p><figure class="highlight plain"><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">index.html</span><br><span class="line">index.html.mine</span><br><span class="line">index.html.r1</span><br><span class="line">index.html.r2</span><br></pre></td></tr></table></figure><p></p><p>解决冲突方法大致有一下几种:</p><p><strong>手工修改index.html文件,然后将当前index.html作为最后提交的版本</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn resolve index.html –-accept working</span><br></pre></td></tr></table></figure><p></p><p><strong>选择base版本,即index.html.rOLDREV作为最后提交的版本</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn resolve index.html –-accept base</span><br></pre></td></tr></table></figure><p></p><p><strong>使用index.html.rNEWREV作为最后提交的版本</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn resolve index.html –-accept theirs-full</span><br></pre></td></tr></table></figure><p></p><p><strong>使用index.html.mine作为最后提交的版本</strong></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">svn resolve index.html –-accept mine-full</span><br><span class="line">// 或者用下面这条命令也可以</span><br><span class="line">// svn resolve index.html –-accept theirs-conflict</span><br></pre></td></tr></table></figure><p></p><h3 id="提交代码">提交代码</h3><p>最后,一切确认没问题了:code review完毕,自己觉得代码满意了;然后也合并完别人的修改并且没有冲突了。那么就提交代码吧:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn commit/ci -m <span class="string">'message'</span></span><br></pre></td></tr></table></figure><p></p><h3 id="导出代码">导出代码</h3><p>你想把你的代码导出,不包含svn版本信息,那么你可以:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">svn <span class="built_in">export</span> svn://xxx.com/repo/branches/<span class="built_in">test</span> folder(本地目录)</span><br></pre></td></tr></table></figure><p></p><h2 id="git篇">Git篇</h2><h3 id="安装之后第一步">安装之后第一步</h3><p>安装 Git 之后,你要做的第一件事情就是去配置你的名字和邮箱,因为每一次提交都需要这些信息:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git config --global user.name <span class="string">"bukas"</span></span><br><span class="line">git config --global user.email <span class="string">"bukas@gmail.com"</span></span><br></pre></td></tr></table></figure><p></p><p>获取Git配置信息,执行以下命令:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --list</span><br></pre></td></tr></table></figure><p></p><h3 id="创建版本库">创建版本库</h3><p>什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkdir testgit && <span class="built_in">cd</span> testgit</span><br><span class="line">git init</span><br></pre></td></tr></table></figure><p></p><p>瞬间Git就把仓库建好了,细心的读者可以发现当前目录下多了一个<code>.git</code>的目录,默认是隐藏的,用<code>ls -ah</code>命令就可以看见。</p><p><img src="http://blog.u.qiniudn.com/uploads/git1.png" alt="git-init"></p><h3 id="把文件添加到版本库">把文件添加到版本库</h3><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">touch readme.md</span><br><span class="line">git add readme.md</span><br></pre></td></tr></table></figure><p></p><p>然后用命令<code>git commit</code>告诉Git把文件提交到仓库:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit -m <span class="string">"wrote a readme file"</span></span><br></pre></td></tr></table></figure><p></p><p>简单解释一下<code>git commit</code>命令,<code>-m</code>后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。</p><p>一次可以<code>add</code>多个不同的文件,以空格分隔:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add a.txt b.txt c.txt</span><br></pre></td></tr></table></figure><p></p><h3 id="仓库状态">仓库状态</h3><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure><p></p><p><code>git status</code>命令可以让我们时刻掌握仓库当前的状态。</p><p>但如果能看看具体修改了什么内容就更好了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff readme.md</span><br></pre></td></tr></table></figure><p></p><h3 id="版本回退">版本回退</h3><p>在实际工作中,我们脑子里怎么可能记得一个几千行的文件每次都改了什么内容,不然要版本控制系统干什么。版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用<code>git log</code>命令查看:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">log</span></span><br></pre></td></tr></table></figure><p></p><p><img src="http://blog.u.qiniudn.com/uploads/git2.png" alt="git-log"></p><p><code>git log</code>命令显示从最近到最远的提交日志。如果嫌输出信息太多,看得眼花缭乱的,可以试试加上<code>--pretty=oneline</code>参数:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">log</span> --pretty=oneline</span><br></pre></td></tr></table></figure><p></p><p><img src="http://blog.u.qiniudn.com/uploads/git3.png" alt="git-log-pretty"></p><p>需要友情提示的是,你看到的一大串类似<code>2e70fd...376315</code>的是<code>commit id</code>(版本号)</p><p>在 Git中,用<code>HEAD</code>表示当前版本,也就是最新的提交<code>commit id</code>,上一个版本就是<code>HEAD^</code>,上上一个版本就是<code>HEAD^^</code>,当然往上100个版本写100个^比较容易数不过来,所以写成<code>HEAD~100</code>。</p><p>现在我们要把当前版本回退到上一个版本,就可以使用<code>git reset</code>命令:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset --hard HEAD^</span><br></pre></td></tr></table></figure><p></p><p>然我们用<code>git log</code>再看看现在版本库的状态,最新的那个版本已经看不到了!好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办?</p><p><img src="http://blog.u.qiniudn.com/uploads/git4.png" alt="git-reset"></p><p>办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,假设找到那个<code>commit id</code>是<code>2e70fdf...</code>,于是就可以指定回到未来的某个版本:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset --hard 2e70fdf</span><br></pre></td></tr></table></figure><p></p><p>版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。</p><p>现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的<code>commit id</code>怎么办?</p><p>Git提供了一个命令<code>git reflog</code>用来记录你的每一次命令:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reflog</span><br></pre></td></tr></table></figure><p></p><p><img src="http://blog.u.qiniudn.com/uploads/git5.png" alt="git-reflog"></p><p>终于舒了口气,于是你看到的<code>commit id</code>是<code>2e70fdf</code>,现在,你又可以乘坐时光机回到未来了。</p><h3 id="工作区和暂存区">工作区和暂存区</h3><p>Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。</p><p>工作区就是你在电脑里能看到的目录,比如我的<code>testgit</code>文件夹就是一个工作区。</p><p>工作区有一个隐藏目录<code>.git</code>,这个不算工作区,而是Git的版本库。</p><p>Git的版本库里存了很多东西,其中最重要的就是称为<code>stage</code>(或者叫<code>index</code>)的暂存区,还有Git为我们自动创建的第一个分支<code>master</code>,以及指向 <code>master</code>的一个指针叫<code>HEAD</code>。</p><p>前面讲了我们把文件往 Git 版本库里添加的时候,是分两步执行的:</p><p>第一步是用<code>git add</code>把文件添加进去,实际上就是把文件修改添加到暂存区;</p><p>第二步是用<code>git commit</code>提交更改,实际上就是把暂存区的所有内容提交到当前分支。</p><p>因为我们创建Git版本库时,Git自动为我们创建了唯一一个<code>master</code>分支,所以现在<code>git commit</code>就是往<code>master</code>分支上提交更改。</p><p>你可以简单理解为,<code>git add</code>命令实际上就是把要提交的所有修改放到暂存区(Stage),然后执行<code>git commit</code>就可以一次性把暂存区的所有修改提交到分支。</p><p>一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的。</p><h3 id="修改与撤销">修改与撤销</h3><p>用<code>git diff HEAD -- readme.md</code>命令可以查看工作区和版本库里面最新版本的区别。</p><p><code>git checkout -- file</code>可以丢弃工作区的修改:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -- readme.md</span><br></pre></td></tr></table></figure><p></p><p>命令<code>git checkout -- readme.md</code>意思就是,把<code>readme.md</code>文件在工作区的修改全部撤销,即让这个文件回到最近一次<code>git commit</code>或<code>git add</code>时的状态。</p><p>当然也可以用<code>git reset</code>命令。</p><h3 id="删除文件">删除文件</h3><p>一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用<code>rm</code>命令删了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rm readme.md</span><br></pre></td></tr></table></figure><p></p><p>这个时候,Git 知道你删除了文件,因此,工作区和版本库就不一致了,<code>git status</code>命令会立刻告诉你哪些文件被删除了。</p><p>现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令<code>git rm</code>删掉,并且<code>git commit</code>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git rm readme.md</span><br><span class="line">git commit -m <span class="string">"remove readme.md"</span></span><br></pre></td></tr></table></figure><p></p><p>现在,文件就从版本库中被删除了。</p><p>另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -- readme.md</span><br></pre></td></tr></table></figure><p></p><h3 id="生成ssh-key">生成SSH key</h3><p>创建 SSH Key。在用户主目录下,看看有没有<code>.ssh</code>目录,如果有,再看看这个目录下有没有<code>id_rsa</code>和<code>id_rsa.pub</code>这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开 Shell(Windows下打开Git Bash),创建SSH Key:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh-keygen -t rsa -C <span class="string">"youremail@example.com"</span></span><br></pre></td></tr></table></figure><p></p><p>你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可。</p><p>如果一切顺利的话,可以在用户主目录里找到<code>.ssh</code>目录,里面有<code>id_rsa</code>和<code>id_rsa.pub</code>两个文件,这两个就是SSH Key的秘钥对,<code>id_rsa</code>是私钥,不能泄露出去,<code>id_rsa.pub</code>是公钥,可以放心地告诉任何人。</p><p>然后登录GitHub(或者其它Git代码托管平台),打开<code>Account settings</code>,<code>SSH Keys</code>页面,点<code>Add SSH Key</code>,填上任意<code>Title</code>,在<code>Key</code>文本框里粘贴<code>id_rsa.pub</code>文件的内容。</p><p>为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。</p><p>当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。</p><h3 id="远程服务器">远程服务器</h3><p>Git 最强大的功能之一是可以有一个以上的远程服务器(另一个事实,你总是可以运行一个本地仓库)。你不一定总是需要写访问权限,你可以从多个服务器中读取(用于合并),然后写到另一个服务器中。添加一个远程服务器很简单:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote add origin(别名,根据爱好命名) git@github.com:bukas/bukas.git</span><br></pre></td></tr></table></figure><p></p><p>如果你想查看远程服务器的相关信息,你可以这样做:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># shows URLs of each remote server</span></span><br><span class="line">git remote -v</span><br><span class="line"></span><br><span class="line"><span class="comment"># gives more details about origin</span></span><br><span class="line">git remote show origin(别名)</span><br></pre></td></tr></table></figure><p></p><p>下一步,就可以把本地库的所有内容推送到远程库上:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push -u origin master</span><br></pre></td></tr></table></figure><p></p><p>把本地库的内容推送到远程,用<code>git push</code>命令,实际上是把当前分支<code>master</code>推送到远程。</p><p>由于远程库是空的,我们第一次推送<code>master</code>分支时,加上了<code>-u</code>参数,Git不但会把本地的<code>master</code>分支内容推送的远程新的<code>master</code>分支,还会把本地的<code>master</code>分支和远程的<code>master</code>分支关联起来,在以后的推送或者拉取时就可以简化命令。</p><p>从现在起,只要本地作了提交,就可以通过命令把本地<code>master</code>分支的最新修改推送至GitHub:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin master</span><br></pre></td></tr></table></figure><p></p><p><strong>SSH警告</strong></p><p>当你第一次使用Git的<code>clone</code>或者<code>push</code>命令连接GitHub时,会得到一个警告:</p><blockquote><p>The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.</p><p>RSA key fingerprint is xx.xx.xx.xx.xx.</p><p>Are you sure you want to continue connecting (yes/no)?</p></blockquote><p>这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认 GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入<code>yes</code>回车即可。</p><h3 id="从远程库克隆">从远程库克隆</h3><p>当已经有一个远程库的时候,我们可以用命令<code>git clone</code>克隆一个本地库:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> git@github.com:<span class="built_in">test</span>/testgit.git</span><br></pre></td></tr></table></figure><p></p><p>你也许还注意到,GitHub给出的地址不止一个,还可以用<code>https://github.com/test/testgit.git</code>这样的地址。实际上Git支持多种协议,默认的<code>git://</code>使用<code>ssh</code>,但也可以使用 <code>https</code>等其他协议。使用<code>https</code>除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放<code>http</code>端口的公司内部就无法使用<code>ssh</code>协议而只能用<code>https</code>。</p><h3 id="创建与合并分支">创建与合并分支</h3><p>首先我们创建<code>dev</code>分支,然后切换到<code>dev</code>分支:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -b dev</span><br></pre></td></tr></table></figure><p></p><p><code>git checkout</code>命令加上<code>-b</code>参数表示创建并切换,相当于以下两条命令:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git branch dev</span><br><span class="line">git checkout dev</span><br></pre></td></tr></table></figure><p></p><p>然后用<code>git branch</code>命令查看当前分支:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch</span><br></pre></td></tr></table></figure><p></p><p>我们在<code>dev</code>分支上进行添加修改操作,然后我们把<code>dev</code>分支的工作成果合并到<code>master</code>分支上:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git checkout master</span><br><span class="line">git merge dev</span><br></pre></td></tr></table></figure><p></p><p><code>git merge</code>命令用于合并指定分支到当前分支。</p><p>注意到<code>git merge</code>的信息里面可能有<code>Fast-forward</code>字样,Git告诉我们,这次合并是“快进模式”,也就是直接把<code>master</code>指向<code>dev</code>的当前提交,所以合并速度非常快。</p><p>当然也不是每次合并都能<code>Fast-forward</code>。</p><p>合并完成后,就可以放心地删除<code>dev</code>分支了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -d dev</span><br></pre></td></tr></table></figure><p></p><p>如果要丢弃一个没有被合并过的分支,可以通过<code>git branch -D <branch></code>强行删除。</p><p>在本地创建和远程分支对应的分支,使用<code>git checkout -b branch-name origin/branch-name</code>,本地和远程分支的名称最好一致;</p><p>建立本地分支和远程分支的关联,使用<code>git branch --set-upstream branch-name origin/branch-name</code>;</p><p>从远程抓取分支,使用<code>git pull</code>,如果有冲突,要先处理冲突。</p><h3 id="解决冲突-v2">解决冲突</h3><p>人生不如意之事十之八九,合并分支往往也不是一帆风顺的。</p><p>有时候我们进行合并的时候,会提示有冲突出现<code>CONFLICT (content)</code>,必须手动解决冲突后再提交。<code>git status</code>也可以告诉我们冲突的文件。</p><p>打开冲突文件我们会看到Git用<code><<<<<<<</code>,<code>=======</code>,<code>>>>>>>></code>标记出不同分支的内容,我们修改后提交:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git add readme.md</span><br><span class="line">git commit -m <span class="string">"conflict fixed"</span></span><br></pre></td></tr></table></figure><p></p><p>用带参数的<code>git log</code>也可以看到分支的合并情况:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">log</span> --graph --pretty=oneline --abbrev-commit</span><br></pre></td></tr></table></figure><p></p><h3 id="分支管理策略">分支管理策略</h3><p>通常,合并分支时,如果可能,Git会用<code>Fast forward</code>模式,但这种模式下,删除分支后,会丢掉分支信息。</p><p>如果要强制禁用<code>Fast forward</code>模式,Git就会在<code>merge</code>时生成一个新的<code>commit</code>,这样,从分支历史上就可以看出分支信息。</p><p>下面我们实战一下<code>--no-ff</code>方式的<code>git merge</code>:</p><p>首先,仍然创建并切换<code>dev</code>分支:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -b dev</span><br></pre></td></tr></table></figure><p></p><p>修改<code>readme.md</code>文件,并提交一个新的<code>commit</code>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git add readme.md</span><br><span class="line">git commit -m <span class="string">"add merge"</span></span><br></pre></td></tr></table></figure><p></p><p>现在,我们切换回<code>master</code>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout master</span><br></pre></td></tr></table></figure><p></p><p>准备合并<code>dev</code>分支,请注意<code>--no-ff</code>参数,表示禁用<code>Fast forward</code>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git merge --no-ff -m <span class="string">"merge with no-ff"</span> dev</span><br></pre></td></tr></table></figure><p></p><p>用一个分支的内容覆盖另一个分支:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// 合并dev,冲突时用dev的代码替换master的代码</span><br><span class="line">git merge -s recursive -X theirs dev</span><br><span class="line">// 合并dev,冲突时用master的代码替换dev的代码</span><br><span class="line">git merge -s recursive -X ours dev</span><br></pre></td></tr></table></figure><p></p><h3 id="bug分支">Bug分支</h3><p>软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。</p><p>当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支<code>issue-101</code>来修复它,但是,等等,当前正在<code>dev</code>上进行的工作还没有提交。</p><p>并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?</p><p>幸好,Git还提供了一个<code>stash</code>功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash</span><br></pre></td></tr></table></figure><p></p><p>现在,用<code>git status</code>查看工作区,就是干净的(除非有没有被 Git 管理的文件),因此可以放心地创建分支来修复bug。</p><p>首先确定要在哪个分支上修复bug,假定需要在<code>master</code>分支上修复,就从<code>master</code>创建临时分支:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git checkout master</span><br><span class="line">git checkout -b issue-101</span><br></pre></td></tr></table></figure><p></p><p>现在修复bug,然后提交:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git add readme.md</span><br><span class="line">git commit -m <span class="string">"fix bug 101"</span></span><br></pre></td></tr></table></figure><p></p><p>修复完成后,切换到<code>master</code>分支,并完成合并,最后删除<code>issue-101</code>分支:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git checkout master</span><br><span class="line">git merge --no-ff -m <span class="string">"merged bug fix 101"</span> issue-101</span><br></pre></td></tr></table></figure><p></p><p>太棒了,原计划两个小时的bug修复只花了5分钟!现在,是时候接着回到<code>dev</code>分支干活了!</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git checkout dev</span><br><span class="line">git status</span><br></pre></td></tr></table></figure><p></p><p>工作区是干净的,刚才的工作现场存到哪去了?用<code>git stash list</code>命令看看:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash list</span><br></pre></td></tr></table></figure><p></p><p>工作现场还在,Git把<code>stash</code>内容存在某个地方了,但是需要恢复一下,有两个办法:</p><p>一是用<code>git stash apply</code>恢复,但是恢复后,<code>stash</code>内容并不删除,你需要用<code>git stash drop</code>来删除;</p><p>另一种方式是用<code>git stash pop</code>,恢复的同时把<code>stash</code>内容也删了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash pop</span><br></pre></td></tr></table></figure><p></p><p>再用<code>git stash list</code>查看,就看不到任何<code>stash</code>内容了。</p><p>你可以多次<code>stash</code>,恢复的时候,先用<code>git stash list</code>查看,然后恢复指定的<code>stash</code>,用命令</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash apply stash@{0}</span><br></pre></td></tr></table></figure><p></p><h3 id="标签管理">标签管理</h3><p>发布一个版本时,我们通常先在版本库中打一个标签,这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。</p><p>命令<code>git tag <tagname></code>用于新建一个标签,默认为<code>HEAD</code>,也可以指定一个<code>commit id</code>。</p><p><code>git tag -a <tagname> -m "blablabla..."</code>可以指定标签信息。</p><p>还可以通过<code>-s</code>用私钥签名一个标签:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -s v0.5 -m <span class="string">"signed version 0.2 released"</span> fec145a</span><br></pre></td></tr></table></figure><p></p><p><code>git tag</code>可以查看所有标签。</p><p>用命令<code>git show <tagname></code>可以查看某个标签的详细信息。</p><p>如果标签打错了,也可以删除:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -d v0.1</span><br></pre></td></tr></table></figure><p></p><p>因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。</p><p>如果要推送某个标签到远程,使用命令<code>git push origin <tagname></code>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin v1.0</span><br></pre></td></tr></table></figure><p></p><p>或者,一次性推送全部尚未推送到远程的本地标签:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin --tags</span><br></pre></td></tr></table></figure><p></p><p>如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -d v0.9</span><br></pre></td></tr></table></figure><p></p><p>然后,从远程删除。删除命令也是<code>push</code>,但是格式如下:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin :refs/tags/v0.9</span><br></pre></td></tr></table></figure><p></p><h3 id="忽略特殊文件">忽略特殊文件</h3><p>在安装Git一节中,我们已经配置了<code>user.name</code> 和<code>user.email</code>,实际上,Git还有很多可配置项。</p><p>比如,让Git显示颜色,会让命令输出看起来更醒目:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global color.ui <span class="literal">true</span></span><br></pre></td></tr></table></figure><p></p><p>有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,每次<code>git status</code>都会显示Untracked files...,有强迫症的童鞋心里肯定不爽。</p><p>好在Git考虑到了大家的感受,这个问题解决起来也很简单,在 Git工作区的根目录下创建一个特殊的<code>.gitignore</code>文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。</p><p>不需要从头写<code>.gitignore</code>文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:<a href="https://github.com/github/gitignore" target="_blank" rel="noopener">https://github.com/github/gitignore</a></p><p>当然也可以配置全局忽略的文件,这样就不用每个项目都加gitignore了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global core.excludesfile <span class="string">'~/.gitignore'</span></span><br></pre></td></tr></table></figure><p></p><h3 id="配置别名">配置别名</h3><p>有没有经常敲错命令?比如<code>git status</code>?<code>status</code>这个单词真心不好记。</p><p>如果敲<code>git st</code>就表示<code>git status</code>那就简单多了,当然这种偷懒的办法我们是极力赞成的。</p><p>我们只需要敲一行命令,告诉Git,以后<code>st</code>就表示<code>status</code>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global alias.st status</span><br></pre></td></tr></table></figure><p></p><p>当然还有别的命令可以简写:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git config --global alias.co checkout</span><br><span class="line">git config --global alias.ci commit</span><br><span class="line">git config --global alias.br branch</span><br></pre></td></tr></table></figure><p></p><p><code>--global</code>参数是全局参数,也就是这些命令在这台电脑的所有Git仓库下都有用。</p><p>在撤销修改一节中,我们知道,命令<code>git reset HEAD file</code>可以把暂存区的修改撤销掉(<code>unstage</code>),重新放回工作区。既然是一个<code>unstage</code>操作,就可以配置一个<code>unstage</code>别名:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global alias.unstage <span class="string">'reset HEAD'</span></span><br></pre></td></tr></table></figure><p></p><p>配置一个<code>git last</code>,让其显示最后一次提交信息:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global alias.last <span class="string">'log -1'</span></span><br></pre></td></tr></table></figure><p></p><p>甚至还有人把<code>lg</code>配置成了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global alias.lg <span class="string">"log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"</span></span><br></pre></td></tr></table></figure><p></p><p><strong>配置文件</strong></p><p>配置Git的时候,加上--global是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。</p><p>配置文件放哪了?每个仓库的Git配置文件都放在<code>.git/config</code>文件中。</p><p>而当前用户的Git配置文件放在用户主目录下的一个隐藏文件<code>.gitconfig</code>中。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2015/09/those-common-svn-and-git-commands/#disqus_thread</comments>
</item>
<item>
<title>配置你的Mac开发环境</title>
<link>https://www.noonme.com/post/2015/07/config-your-mac-development-environment/</link>
<guid>https://www.noonme.com/post/2015/07/config-your-mac-development-environment/</guid>
<pubDate>Sun, 12 Jul 2015 15:44:23 GMT</pubDate>
<description>
<p><code>Mac</code>是目前大多数开发者们首选的开发机器,下面就让我们一起来配置你的Mac开发环境吧。</p>
</description>
<content:encoded><![CDATA[<p><code>Mac</code>是目前大多数开发者们首选的开发机器,下面就让我们一起来配置你的Mac开发环境吧。</p><a id="more"></a><h2 id="xcode">Xcode</h2><p>如果你使用<code>Mac</code>做开发,<code>Xcode</code>是必不可少的,不管你是不是做<code>ios/osx</code>开发,你可能都需要安装<code>Xcode</code>,因为它会提供一些基础的环境,如<code>svn</code>、<code>git</code>等,你只需要去<code>Mac App Store</code>安装就可以了。</p><h2 id="homebrew-homebrew-cask">homebrew && homebrew-cask</h2><p><code>Mac</code>下面的应用安装体验其实是挺糟糕的,特别是在天朝,<code>App Store</code>的打开速度实在是慢,不过好在有人发明了一个叫<code>homebrew</code>的东西,通过简单的命令就可以安装好你想要的应用程序,十分方便,而且通过<code>homebrew</code>安装应用还有一些其他的好处,比如说权限问题,或者说应用程序的备份恢复,都会变得十分方便。安装<code>homebrew</code>也是需要先安装<code>Xcode</code>的的哦。</p><p>打开<code>homebrew</code>的官网<a href="http://brew.sh/" target="_blank" rel="noopener">http://brew.sh/</a>,我们会看到一条安装命令,通过这条命令我们就可以安装好<code>homebrew</code>了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ruby -e <span class="string">"<span class="variable">$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)</span>"</span></span><br></pre></td></tr></table></figure><p></p><p>接下来我们就可以用命令来安装应用程序了,如:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install wget</span><br></pre></td></tr></table></figure><p></p><p>但是还不够完美,因为<code>homebrew</code>本身可直接安装的应用程序不是很多,这时候我们就需要安装<code>homebrew-cask</code>h<a href="http://caskroom.io/" target="_blank" rel="noopener">ttp://caskroom.io/</a>来完善下:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install caskroom/cask/brew-cask</span><br></pre></td></tr></table></figure><p></p><p>接下来我们就可以通过<code>brew cask</code>来安装更多的应用了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">brew cask install google-chrome</span><br><span class="line">brew cask install qq</span><br><span class="line">brew cask install alfred</span><br><span class="line">brew cask install nodejs</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p></p><p><code>homebrew-cask</code>提供的应用程序很多,但是都是稳定版本的,如果想要安装测试版的应用,你还需要安装<code>homebrew-cask-versions</code><a href="https://github.com/caskroom/homebrew-versions" target="_blank" rel="noopener">https://github.com/caskroom/homebrew-versions</a>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew tap caskroom/versions</span><br></pre></td></tr></table></figure><p></p><p>安装完成之后,你就可以安装测试版本的应用了,如:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">brew cask install google-chrome-canary</span><br><span class="line">brew cask install sublime-text3</span><br></pre></td></tr></table></figure><p></p><p>如果你不太清楚你的应用名字,可以搜索,如:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew cask search baidu</span><br></pre></td></tr></table></figure><p></p><p>或者你也可以去<a href="http://caskroom.io/search" target="_blank" rel="noopener">http://caskroom.io/search</a>搜索,或者去<a href="https://github.com/caskroom/homebrew-cask/tree/master/Casks" target="_blank" rel="noopener">https://github.com/caskroom/homebrew-cask/tree/master/Casks</a>这里查看。</p><p>还有其它可以使用的命令:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">// 检测homebrew环境</span><br><span class="line">brew doctor && brew cask doctor</span><br><span class="line">// 更新 清理</span><br><span class="line">brew update && brew upgrade brew-cask && brew cleanup && brew cask cleanup</span><br><span class="line">// 拆卸</span><br><span class="line">brew cask uninstall qq</span><br></pre></td></tr></table></figure><p></p><p>详细的用法可以去这里:<a href="https://github.com/caskroom/homebrew-cask/blob/master/USAGE.md" target="_blank" rel="noopener">https://github.com/caskroom/homebrew-cask/blob/master/USAGE.md</a></p><h2 id="iterm2">iTerm2</h2><p>系统自带的终端功能比较弱,因此我们安装功能更强大的<code>iterm2</code>来替代它,通过<code>brew-cask</code>安装:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew cask install iterm2</span><br></pre></td></tr></table></figure><p></p><p>你可以把<code>iTerm2</code>设置为默认终端:</p><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(菜单栏)iTerm -> Make iTerm2 Default Term</span><br></pre></td></tr></table></figure><p></p><p>然后可以去这里选择一套配色方案:<a href="http://iterm2colorschemes.com/" target="_blank" rel="noopener">http://iterm2colorschemes.com/</a>(你也可以自己定制配色方案),下载完成后依次选择:<code>iTerm->Preferences->Profiles->Colors</code>,然后选择下面的<code>Load Presets->Import</code>,选择下载好的<code>schemes</code>文件夹里面的<code>.itermcolors</code>后缀的文件导入主题即可选择使用。</p><p><code>iTerm2</code>有一些常用的快捷键:</p><p></p><figure class="highlight plain"><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">⌘ + n // 新建term窗口</span><br><span class="line">⌘ + t // 新建标签页</span><br><span class="line">⌘ + w // 关闭标签页或者窗口</span><br><span class="line">⌘ + d / ⌘ + shift + d // 分屏显示</span><br><span class="line">⌘ + 数字 / ⌘ + <- / -> // tab标签页之间切换</span><br><span class="line">⌘ + ; // 自动补全</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p></p><p>还可以自定义一些快捷键,在<code>iTerm->Preferences->Keys</code>里面设置,你可以通过快捷键来显示/隐藏<code>iTerm2</code>,还可以通过快捷键键入命令,如在<code>Global Shortcut Keys</code>里面增加一个自定义的快捷键,<code>Action</code>选择<code>Send Text</code>输入你想要通过快捷键输入的命令就可以了。</p><h2 id="zsh">Zsh</h2><p><code>zsh</code>是个什么东东呢,它与<code>bash</code>到底有什么区别?有人如是说:</p><blockquote><p><code>zsh</code>是一种更强大的、被成为“终极”的<code>shell</code>,意思是<code>shell</code>能具备的功能它基本都提供了。<code>zsh</code>基本上是兼容<code>bash</code>,有些小细节不同,如果写脚本时需要注意。具体参见:<a href="http://www.soimort.org/posts/163/" target="_blank" rel="noopener">Zsh vs. Bash:不完全对比解析</a>。</p></blockquote><p><code>Mac</code>系统自带了<code>zsh</code>,一般不是最新版,所以我们通过<code>Homebrew</code>来安装最新的:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install zsh</span><br></pre></td></tr></table></figure><p></p><p>可通过<code>zsh --version</code>命令查看<code>zsh</code>的版本。</p><p>我们需要把<code>bash</code>换成<code>zsh</code>:</p><ul><li>在/etc/shells文件末尾添加:</li></ul><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/usr/<span class="built_in">local</span>/bin/zsh</span><br></pre></td></tr></table></figure><p></p><ul><li>执行如下命令:</li></ul><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chsh -s /usr/<span class="built_in">local</span>/bin/zsh</span><br></pre></td></tr></table></figure><p></p><ul><li>最后记得将<code>~/.bash_prorile</code>或者<code>~/.profile</code>等配置拷贝到<code>~/.zshrc</code>文件中。</li></ul><p><code>zsh</code>功能强大,配置项也很多,不过好在有一个<a href="http://ohmyz.sh/" target="_blank" rel="noopener">oh-my-zsh</a>的东东,它是一个开源的<code>zsh</code>配置管理框架,提供了大量实用的功能,主题等。通过如下命令安装:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh</span><br></pre></td></tr></table></figure><p></p><p>然后你就可以选择自己喜欢的<a href="https://github.com/robbyrussell/oh-my-zsh/wiki/Themes" target="_blank" rel="noopener">主题</a>。只需要修改<code>~/.zshrc</code>文件中的<code>ZSH_THEME</code>即可。</p><p>还可以为一些命令配置别名:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim ~/.zshrc</span><br></pre></td></tr></table></figure><p></p><p>然后加入别名:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">alias</span> subl=<span class="string">"open -a ~/Applications/Sublime\\ Text\\ 3.app"</span></span><br></pre></td></tr></table></figure><p></p><p>这样就可以通过命令<code>subl test.md</code>来打开文件了。</p><h2 id="tmux">tmux</h2><p>首先<code>tmux</code>到底是个什么玩意?按照<a href="http://tmux.github.io/" target="_blank" rel="noopener">tmux官网</a>翻译过来:</p><blockquote><p>TMUX是终端复用器</p></blockquote><p>简单来说就是一种管理窗口的程序,那么问题来了:为什么要用<code>tmux</code>,支持多标签?支持窗体内部Panel的分割?但是这些iTerm2都支持啊,那为何还要用<code>tmux</code>呢?看看下面的使用场景。</p><blockquote><p>公司服务器上调试程序,开了一堆窗口。出去吃了个饭,发现<code>ssh</code>超时了,<code>broken pipe</code>。重头开始...FK!如果你之前使用了<code>tmux</code>就不会有这样的问题,<code>attach</code>就能找回原来打开的那些窗口。</p></blockquote><p>首先安装<code>tmux</code></p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install tmux</span><br></pre></td></tr></table></figure><p></p><p>然后就可以在命令行操作<code>tmux</code>了:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">tmux // 创建一个默认会话,默认会话名从0开始</span><br><span class="line">tmux new-session -s <span class="built_in">test</span> // 创建一个名字叫<span class="built_in">test</span>的会话</span><br><span class="line">tmux new-session -s <span class="built_in">test</span> -d // 在后台创建<span class="built_in">test</span>会话</span><br><span class="line">tmux list-sessions / tmux ls // 列出所有正在运行的会话</span><br><span class="line">tmux a -t <span class="built_in">test</span> // 进入<span class="built_in">test</span>会话</span><br><span class="line">tmux <span class="built_in">kill</span>-session -t <span class="built_in">test</span> // 关闭<span class="built_in">test</span>会话</span><br><span class="line">tmux <span class="built_in">kill</span>-server // 关闭tmux服务,所有的会话将被关闭</span><br></pre></td></tr></table></figure><p></p><p>下面是<code>tmux</code>的一些快捷键操作:</p><p><code>Prefix-Command</code>前置操作:所有下面介绍的快捷键,都必须以前置操作开始。<code>tmux</code>默认的前置操作是<code>ctrl + b</code>。例如,我们想要新建一个窗体,就需要先在键盘上摁下<code>ctrl + b</code>,松开后再摁下<code>n</code>键。</p><p>下面所有的<code>prefix</code>均代表<code>ctrl + b</code></p><p><strong>Session相关操作</strong></p><table><thead><tr><th>操作</th><th style="text-align:center">快捷键</th></tr></thead><tbody><tr><td>查看/切换session</td><td style="text-align:center"><code>prefix s</code></td></tr><tr><td>离开Session</td><td style="text-align:center"><code>prefix d</code></td></tr><tr><td>重命名当前Session</td><td style="text-align:center"><code>prefix $</code></td></tr></tbody></table><p><strong>Window相关操作</strong></p><table><thead><tr><th>操作</th><th style="text-align:center">快捷键</th></tr></thead><tbody><tr><td>切换到下一个窗格</td><td style="text-align:center"><code>prefix o</code></td></tr><tr><td>查看所有窗格的编号</td><td style="text-align:center"><code>prefix q</code></td></tr><tr><td>垂直拆分出一个新窗格</td><td style="text-align:center"><code>prefix “</code></td></tr><tr><td>水平拆分出一个新窗格</td><td style="text-align:center"><code>prefix %</code></td></tr><tr><td>暂时把一个窗体放到最大</td><td style="text-align:center"><code>prefix z</code></td></tr></tbody></table><p>默认的tmux风格比较朴素甚至有些丑陋。如果希望做一些美化和个性化配置的话,建议使用<a href="https://github.com/gpakosz/.tmux" target="_blank" rel="noopener">gpakosz的tmux配置</a>。它的本质是一个<code>tmux</code>配置文件,安装方式也很简单:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~</span><br><span class="line">rm -rf .tmux</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/gpakosz/.tmux.git</span><br><span class="line">ln -s .tmux/.tmux.conf</span><br><span class="line">cp .tmux/.tmux.conf.local .</span><br></pre></td></tr></table></figure><p></p>]]></content:encoded>
<comments>https://www.noonme.com/post/2015/07/config-your-mac-development-environment/#disqus_thread</comments>
</item>
<item>
<title>微信WebView下载应用解决方案</title>
<link>https://www.noonme.com/post/2015/06/download-app-in-weixin/</link>
<guid>https://www.noonme.com/post/2015/06/download-app-in-weixin/</guid>
<pubDate>Wed, 17 Jun 2015 13:44:23 GMT</pubDate>
<description>
<p>众所周知,微信会屏蔽掉直接的app下载链接,那么有没有办法可以让用户能够在微信里面点一个链接或者识别二维码下载应用呢?答案是肯定的。</p>
</description>
<content:encoded><![CDATA[<p>众所周知,微信会屏蔽掉直接的app下载链接,那么有没有办法可以让用户能够在微信里面点一个链接或者识别二维码下载应用呢?答案是肯定的。</p><a id="more"></a><h2 id="原始方案">原始方案</h2><p>其实之前发现可以通过腾讯的应用宝让用户能够在微信<code>webview</code>里面下载应用,具体的做法就是:</p><ol><li><p>将你的应用上传到应用宝:<a href="http://wiki.open.qq.com/index.php?title=mobile/%E5%BA%94%E7%94%A8%E5%AE%9D%E5%BE%AE%E4%B8%8B%E8%BD%BD" target="_blank" rel="noopener">应用宝微下载</a></p></li><li><p>在手机端打开应用宝,找到你的应用,然后点击右上角的分享就可以看到地址了,如:</p><p><img src="http://blog.u.qiniudn.com/uploads/weix-app-down.png" alt="image"></p></li></ol><p>上面复制出来的链接就可以作为下载链接,不过只能是安卓,如果ios话直接AppStore的链接了,因此还要作客户端类型的判断来使用不同的url。</p><p>而且上面这种方案还有一个问题就是要多一步,不能直接进行下载操作。</p><h2 id="进一步方案">进一步方案</h2><p>打开应用宝的官网,发现其官网也有应用的下载链接,既然是腾讯的东西,肯定不会屏蔽下载链接的,果然如此,程序员就是程序员,打开右键查看源代码<code>ctrl+f</code>搜索<code>appId</code>,果然搜索到了下载链接:</p><p><img src="http://blog.u.qiniudn.com/uploads/weixin-down-st.png" alt="image"></p><p>于是可以直接点击下载了,省去了一步,但是任然没有解决<code>ios</code>和<code>andriod</code>需要分别处理的问题。</p><h2 id="终极方案">终极方案</h2><p>如果你有尝试通过PC端浏览器打开<strong>原始方案</strong>里面的链接的话,它会跳转到QQ空间的应用中心里面,其中有一个二维码,解析其内容或者用非微信QQ的二维码扫描工具扫描:</p><p><img src="http://blog.u.qiniudn.com/uploads/weinxin-down-all.png" alt="image"></p><p>解析出来的url是这样的:</p><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://fusion.qq.com/app_download?appid=100723839&platform=qzone&via=QZ.MOBILEDETAIL.QRCODE&u=123456</span><br></pre></td></tr></table></figure><p></p><p>通过这个url就可以直接下载应用了,它会自动判断是安卓还是ios分别跳转到不同的下载链接,当然前提是你需要在腾讯应用中心里面填写IOS的信息。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2015/06/download-app-in-weixin/#disqus_thread</comments>
</item>
<item>
<title>iframe从光标处插入图片</title>
<link>https://www.noonme.com/post/2015/06/iframe-insert-picture-cursor/</link>
<guid>https://www.noonme.com/post/2015/06/iframe-insert-picture-cursor/</guid>
<pubDate>Thu, 11 Jun 2015 02:17:12 GMT</pubDate>
<description>
<p>最近改了一个很久之前的编辑器的bug:ie浏览器不能在光标处插入图片,记录在此。</p>
</description>
<content:encoded><![CDATA[<p>最近改了一个很久之前的编辑器的bug:ie浏览器不能在光标处插入图片,记录在此。</p><a id="more"></a><p>编辑器很古老,代码混乱,是通过开启<code>iframe</code>的<code>document.designMode='on'</code>来实现编辑的。</p><p>首先需要在<code>iframe</code>加载之后开启<code>designMode</code>,后监听<code>mouseup</code>和<code>keyup</code>事件来记录光标位置(切记要在<code>designMode</code>开启之后监听,不然ie上面是监听不到的)</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> addEvent = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">if</span>(<span class="built_in">document</span>.addEventListener){</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">type, el, fn</span>)</span>{</span><br><span class="line">el.addEventListener(type, fn, <span class="literal">false</span>);</span><br><span class="line">}</span><br><span class="line">}<span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">document</span>.attachEvent){</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">type, el, fn</span>)</span>{</span><br><span class="line">el.attachEvent(<span class="string">'on'</span> + type, fn);</span><br><span class="line">}</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">type, el, fn</span>) </span>{</span><br><span class="line"> el[<span class="string">'on'</span> + type] = fn;</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">var</span> iframeNode = <span class="built_in">window</span>.frames[<span class="string">'editIframe'</span>];</span><br><span class="line"></span><br><span class="line">addEvent(iframeNode, <span class="string">'load'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">var</span> iframeDoc = iframeNode.contentDocument || iframeNode.document;</span><br><span class="line"></span><br><span class="line">iframeDoc.designMode=<span class="string">'on'</span>;</span><br><span class="line"></span><br><span class="line">addEvent(iframeDoc, <span class="string">'mouseup'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> saveSelection();</span><br><span class="line"> });</span><br><span class="line"> addEvent(iframeDoc, <span class="string">'keyup'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> saveSelection();</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//获取和记录光标</span></span><br><span class="line"> <span class="keyword">var</span> currentRange, supportRange = <span class="keyword">typeof</span> <span class="built_in">document</span>.createRange === <span class="string">'function'</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">getCurrentRange</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> selection, range;</span><br><span class="line"> <span class="keyword">if</span>(supportRange){</span><br><span class="line"> selection = iframeDoc.getSelection();</span><br><span class="line"> <span class="keyword">if</span> (selection.getRangeAt && selection.rangeCount) {</span><br><span class="line"> range = iframeDoc.getSelection().getRangeAt(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> range = iframeDoc.selection.createRange();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> range;</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">saveSelection</span>(<span class="params"></span>) </span>{</span><br><span class="line"> currentRange = getCurrentRange();</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">restoreSelection</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(!currentRange){</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> selection, range;</span><br><span class="line"> iframeDoc.body.focus();</span><br><span class="line"> <span class="keyword">if</span>(supportRange){</span><br><span class="line"> selection = iframeDoc.getSelection();</span><br><span class="line"> selection.removeAllRanges();</span><br><span class="line"> selection.addRange(currentRange);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> currentRange.select();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//插入图片</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">insertImg</span>(<span class="params">html</span>)</span>{</span><br><span class="line"> restoreSelection();</span><br><span class="line">iframeDoc.execCommand(<span class="string">'insertImage'</span>, <span class="literal">false</span>, html);</span><br><span class="line">saveSelection();</span><br><span class="line">}</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><blockquote><p>相关资料</p></blockquote><ul><li><a href="http://www.cnblogs.com/TheViper/p/4303158.html" target="_blank" rel="noopener">编辑器从光标处插入图片(失去焦点后仍然可以在原位置插入)</a></li><li>但是后来发现其中的<code>setEndPoint</code>方法在ie8报参数无效的错误,但是貌似如果编辑器对象是<code>input</code>的话好像就不会报错,后来就找到了这篇博文:<a href="http://www.cnblogs.com/opencoder/articles/1459010.html" target="_blank" rel="noopener">TextRange对象的setEndPoint方法和compareEndPoints方法</a></li><li>Web Api 接口Range(W3C):<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Range" target="_blank" rel="noopener">https://developer.mozilla.org/zh-CN/docs/Web/API/Range</a></li><li>Traversal and Range(IE):<a href="https://msdn.microsoft.com/zh-cn/library/ms536745(v=vs.85).aspx" target="_blank" rel="noopener">https://msdn.microsoft.com/zh-cn/library/ms536745(v=vs.85).aspx</a></li></ul>]]></content:encoded>
<comments>https://www.noonme.com/post/2015/06/iframe-insert-picture-cursor/#disqus_thread</comments>
</item>
<item>
<title>JS混淆加密工具</title>
<link>https://www.noonme.com/post/2015/03/JS%E6%B7%B7%E6%B7%86%E5%8A%A0%E5%AF%86%E5%B7%A5%E5%85%B7/</link>
<guid>https://www.noonme.com/post/2015/03/JS%E6%B7%B7%E6%B7%86%E5%8A%A0%E5%AF%86%E5%B7%A5%E5%85%B7/</guid>
<pubDate>Sat, 21 Mar 2015 07:03:24 GMT</pubDate>
<description>
<p>H5小游戏愈来愈火爆,但是由于源代码是暴露的,因此很容易被复制。最近搜集了一些js混淆的工具,暂时先这样列在这里吧。</p>
</description>
<content:encoded><![CDATA[<p>H5小游戏愈来愈火爆,但是由于源代码是暴露的,因此很容易被复制。最近搜集了一些js混淆的工具,暂时先这样列在这里吧。</p><a id="more"></a><p><a href="https://github.com/mishoo/UglifyJS2" target="_blank" rel="noopener">uglifyjs</a></p><p><a href="https://github.com/google/closure-compiler" target="_blank" rel="noopener">closure compiler</a></p><p><a href="https://github.com/mikrofusion/gulp-obfuscate" target="_blank" rel="noopener">gulp-obfuscate</a></p><p><a href="http://www.javascriptobfuscator.com" target="_blank" rel="noopener">http://www.javascriptobfuscator.com</a></p><p><a href="https://jscrambler.com" target="_blank" rel="noopener">https://jscrambler.com</a></p><p><a href="http://www.xidea.org/project/jsa/" target="_blank" rel="noopener">http://www.xidea.org/project/jsa/</a></p><p><a href="https://github.com/aemkei/jsfuck" target="_blank" rel="noopener">https://github.com/aemkei/jsfuck</a></p><p><a href="http://ucren.com/blog/archives/549" target="_blank" rel="noopener">“短”化你的代码</a></p><p><a href="https://github.com/zswang/jfogs" target="_blank" rel="noopener">https://github.com/zswang/jfogs</a></p>]]></content:encoded>
<comments>https://www.noonme.com/post/2015/03/JS%E6%B7%B7%E6%B7%86%E5%8A%A0%E5%AF%86%E5%B7%A5%E5%85%B7/#disqus_thread</comments>
</item>
<item>
<title>CSS居中</title>
<link>https://www.noonme.com/post/2015/03/%E8%B0%88%E8%B0%88css%E5%B1%85%E4%B8%AD/</link>
<guid>https://www.noonme.com/post/2015/03/%E8%B0%88%E8%B0%88css%E5%B1%85%E4%B8%AD/</guid>
<pubDate>Sun, 08 Mar 2015 12:51:56 GMT</pubDate>
<description>
<p>对于已知宽高的元素,无论垂直、水平居中相对来说都是比较容易的,因此主要是谈谈对于未知宽高的元素的水平垂直居中问题。</p>
</description>
<content:encoded><![CDATA[<p>对于已知宽高的元素,无论垂直、水平居中相对来说都是比较容易的,因此主要是谈谈对于未知宽高的元素的水平垂直居中问题。</p><a id="more"></a><p><img src="http://blog.u.qiniudn.com/uploads/center.jpg" alt></p><h2 id="transform">transform</h2><p>利用css3的transform实现对于未知宽高的元素的水平/垂直居中是很容易的</p><p></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><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></pre></td><td class="code"><pre><span class="line">// 水平/垂直居中</span><br><span class="line">// css</span><br><span class="line"><span class="selector-tag">html</span>,<span class="selector-tag">body</span>,<span class="selector-class">.container</span>{</span><br><span class="line"><span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"><span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.container</span>{</span><br><span class="line"><span class="attribute">position</span>: relative;</span><br><span class="line"><span class="attribute">background</span>: <span class="number">#aaa</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.center</span>{</span><br><span class="line"><span class="attribute">position</span>: absolute;</span><br><span class="line"><span class="attribute">left</span>: <span class="number">50%</span>;</span><br><span class="line"><span class="attribute">top</span>: <span class="number">50%</span>;</span><br><span class="line"><span class="attribute">-webkit-transform</span>: <span class="built_in">translate3d</span>(-50%, -50%, 0);</span><br><span class="line"><span class="attribute">-moz-transform</span>: <span class="built_in">translate3d</span>(-50%, -50%, 0);</span><br><span class="line"><span class="attribute">-ms-transform</span>: <span class="built_in">translate3d</span>(-50%, -50%, 0);</span><br><span class="line"><span class="attribute">transform</span>: <span class="built_in">translate3d</span>(-50%, -50%, 0);</span><br><span class="line"><span class="attribute">background</span>: <span class="number">#000</span>;</span><br><span class="line"><span class="attribute">color</span>: <span class="number">#fff</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><p></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></pre></td><td class="code"><pre><span class="line">// html</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">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">"center"</span>></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<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><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br></pre></td></tr></table></figure><p></p><h2 id="flexbox">flexbox</h2><p></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><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">// css</span><br><span class="line"><span class="selector-tag">html</span>,<span class="selector-tag">body</span>,<span class="selector-class">.container</span>{</span><br><span class="line"><span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"><span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.container</span>{</span><br><span class="line"><span class="attribute">display</span>: -webkit-box; <span class="comment">/* OLD: Safari, iOS, Android browser, older WebKit browsers. */</span></span><br><span class="line"> <span class="attribute">display</span>: -moz-box; <span class="comment">/* OLD: Firefox (buggy) */</span></span><br><span class="line"> <span class="attribute">display</span>: -ms-flexbox; <span class="comment">/* MID: IE 10 */</span></span><br><span class="line"> <span class="attribute">display</span>: -webkit-flex; <span class="comment">/* NEW, Chrome 21–28, Safari 6.1+ */</span></span><br><span class="line"> <span class="attribute">display</span>: flex; <span class="comment">/* NEW: IE11, Chrome 29+, Opera 12.1+, Firefox 22+ */</span></span><br><span class="line"></span><br><span class="line"> <span class="attribute">-webkit-box-align</span>: center;</span><br><span class="line"> <span class="attribute">-moz-box-align</span>: center; <span class="comment">/* OLD… */</span></span><br><span class="line"> <span class="attribute">-ms-flex-align</span>: center; <span class="comment">/* You know the drill now… */</span></span><br><span class="line"> <span class="attribute">-webkit-align-items</span>: center;</span><br><span class="line"> <span class="attribute">align-items</span>: center;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">-webkit-box-pack</span>: center; <span class="attribute">-moz-box-pack</span>: center;</span><br><span class="line"> <span class="attribute">-ms-flex-pack</span>: center;</span><br><span class="line"> <span class="attribute">-webkit-justify-content</span>: center;</span><br><span class="line"> <span class="attribute">justify-content</span>: center;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.center</span>{</span><br><span class="line"><span class="attribute">display</span>: -webkit-box;</span><br><span class="line"><span class="attribute">display</span>: -moz-box;</span><br><span class="line"> <span class="attribute">display</span>: -ms-flexbox;</span><br><span class="line"> <span class="attribute">display</span>: -webkit-flex;</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">-webkit-box-align</span>: center;</span><br><span class="line"> <span class="attribute">-moz-box-align</span>: center;</span><br><span class="line"> <span class="attribute">-ms-flex-align</span>: center;</span><br><span class="line"> <span class="attribute">-webkit-align-items</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><p></p><p></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></pre></td><td class="code"><pre><span class="line">// html</span><br><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">"center"</span>></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<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></p><p>想要查看flexbox的兼容性,请去这里:<a href="http://caniuse.com/flexbox" target="_blank" rel="noopener">Flexible Box Layout Module</a></p><h2 id="table">table</h2><p></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></pre></td><td class="code"><pre><span class="line">// css</span><br><span class="line"><span class="selector-tag">html</span>,<span class="selector-tag">body</span>,<span class="selector-class">.center</span>{</span><br><span class="line"><span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"><span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.center</span> <span class="selector-tag">td</span>{</span><br><span class="line"><span class="attribute">vertical-align</span>: middle;</span><br><span class="line"><span class="attribute">text-align</span>: center;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><p></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></pre></td><td class="code"><pre><span class="line">// html</span><br><span class="line"><span class="tag"><<span class="name">table</span> <span class="attr">class</span>=<span class="string">"center"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">tbody</span>></span><span class="tag"><<span class="name">tr</span>></span></span><br><span class="line"><span class="tag"><<span class="name">td</span>></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"></<span class="name">td</span>></span></span><br><span class="line"><span class="tag"></<span class="name">tr</span>></span><span class="tag"></<span class="name">tbody</span>></span></span><br><span class="line"><span class="tag"></<span class="name">table</span>></span></span><br></pre></td></tr></table></figure><p></p><h2 id="table-cell">table-cell</h2><p>既然table能实现,自然也就会想到将<code>display</code>设置为<code>table</code>来实现。当然,该方案是有局限性的,因为IE8以下的浏览器不支持display的table系value,所以你只能在IE8及以上浏览器以及非IE浏览器下才能看到效果。</p><p></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></pre></td><td class="code"><pre><span class="line">// CSS</span><br><span class="line"><span class="selector-tag">html</span>,<span class="selector-tag">body</span>,<span class="selector-class">.container</span>{</span><br><span class="line"><span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"><span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.container</span>{</span><br><span class="line"><span class="attribute">display</span>: table;</span><br><span class="line"><span class="attribute">text-align</span>: center;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.center</span>{</span><br><span class="line"><span class="attribute">display</span>: table-cell;</span><br><span class="line"><span class="attribute">vertical-align</span>: middle;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><p></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></pre></td><td class="code"><pre><span class="line">// html</span><br><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">"center"</span>></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<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></p><h2 id="inline-block">inline-block</h2><p></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><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">// css</span><br><span class="line"><span class="selector-tag">html</span>,<span class="selector-tag">body</span>,<span class="selector-class">.container</span>{</span><br><span class="line"><span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"><span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.container</span>{</span><br><span class="line"><span class="attribute">text-align</span>: center;</span><br><span class="line"><span class="attribute">font-size</span>: <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.container</span><span class="selector-pseudo">:after</span>,<span class="selector-class">.container</span> <span class="selector-tag">span</span>{</span><br><span class="line"><span class="attribute">display</span>:inline-block;</span><br><span class="line">*display:inline;</span><br><span class="line">*<span class="selector-tag">zoom</span><span class="selector-pseudo">:1</span>;</span><br><span class="line"><span class="selector-tag">width</span><span class="selector-pseudo">:0</span>;</span><br><span class="line"><span class="selector-tag">height</span><span class="selector-pseudo">:100</span>%;</span><br><span class="line"><span class="selector-tag">vertical-align</span><span class="selector-pseudo">:middle</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.container</span><span class="selector-pseudo">:after</span>{</span><br><span class="line"><span class="attribute">content</span>:<span class="string">""</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.center</span>{</span><br><span class="line"><span class="attribute">display</span>:inline-block;</span><br><span class="line">*display:inline;</span><br><span class="line">*<span class="selector-tag">zoom</span><span class="selector-pseudo">:1</span>;</span><br><span class="line"><span class="selector-tag">vertical-align</span><span class="selector-pseudo">:middle</span>;</span><br><span class="line"><span class="selector-tag">font-size</span><span class="selector-pseudo">:16px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><p></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></pre></td><td class="code"><pre><span class="line">// html</span><br><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">"center"</span>></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"><<span class="name">br</span>/></span>我居中<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="comment"><!--[if lt IE 8]><span></span><![endif]--></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p></p><p>因为使用<code>inline-block</code>会有间隙,所以这里设置父级<code>font-size:0</code>来消除间隙。由于ie8以下浏览器不支持伪对象<code>::after</code>,于是我们通过IE条件注释为IE8以下浏览器新增一个额外元素<code>span</code>,其作用等同<code>inline-block</code>中的<code>::after</code>。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2015/03/%E8%B0%88%E8%B0%88css%E5%B1%85%E4%B8%AD/#disqus_thread</comments>
</item>
<item>
<title>抓包神器Fiddler</title>
<link>https://www.noonme.com/post/2015/03/%E6%8A%93%E5%8C%85%E7%A5%9E%E5%99%A8Fiddler/</link>
<guid>https://www.noonme.com/post/2015/03/%E6%8A%93%E5%8C%85%E7%A5%9E%E5%99%A8Fiddler/</guid>
<pubDate>Sat, 07 Mar 2015 07:49:27 GMT</pubDate>
<description>
<p>Fiddler是一款免费且功能强大的数据包抓取软件,它能够记录所有客户端和服务器间的HTTP(S)请求,允许你监视,设置断点,甚至修改输入输出数据,Fiddler包含了一个强大的基于事件脚本的子系统,并且能够使用.net框架语言扩展。所以无论你是从事什么开发,哪种语言,只要你想了解HTTP,这个工具就值得你去了解,而且更重要的一点,这个工具是免费的。</p>
</description>
<content:encoded><![CDATA[<p>Fiddler是一款免费且功能强大的数据包抓取软件,它能够记录所有客户端和服务器间的HTTP(S)请求,允许你监视,设置断点,甚至修改输入输出数据,Fiddler包含了一个强大的基于事件脚本的子系统,并且能够使用.net框架语言扩展。所以无论你是从事什么开发,哪种语言,只要你想了解HTTP,这个工具就值得你去了解,而且更重要的一点,这个工具是免费的。</p><a id="more"></a><p><img src="http://blog.u.qiniudn.com/uploads/fiddler.png" alt="image"></p><p>首先给出官网的下载链接:</p><p><a href="http://www.telerik.com/download/fiddler" target="_blank" rel="noopener">Fiddler下载</a></p><p>Fiddler目前没有Mac版,因此在Mac上用了<strong>Charles</strong>,但是这个软件貌似无法查看到WebView里面的请求,所以我Parallels Desktop装了一个Windows的虚拟机,然后设置虚拟机网络连接类型为桥接网络即可。</p><p><img src="http://blog.u.qiniudn.com/uploads/fiddler0.png" alt="image"></p><h2 id="查看请求">查看请求</h2><p>启动Fiddler客户端就可以查看到本地的网络请求了(如果浏览器配置了代理,如chrome代理插件SwitchyOmega/Switchy,请选择使用系统代理模式):</p><p><img src="http://blog.u.qiniudn.com/uploads/fiddler1.png" alt="image"></p><p>上图中我用浏览器打开百度首页,即可以在Fiddler中看到所有的网络请求情况。</p><h2 id="修改请求">修改请求</h2><p>依次选择菜单栏:Rules-> Automatic Breakpoint ->Before Requests,如下图:</p><p><img src="http://blog.u.qiniudn.com/uploads/fiddler2.png" alt="image"></p><p>此时我们再在浏览器打开一个请求,它会自动中断请求:</p><p><img src="http://blog.u.qiniudn.com/uploads/fiddler3.png" alt="image"></p><p>当我们点击<strong>Run to Completion</strong>的时候它才会真正的把请求发送出去,那么我们就可以在真正发送请求之前改掉请求数据。</p><p>接着我们来试试在登录知乎的时候修改一下登录数据(知乎是明文密码登录。。。,估计大批网站都是明文登录,所以如果在网络传输过程中被劫持,后果你懂的。。。)</p><p><img src="http://blog.u.qiniudn.com/uploads/fiddler4.png" alt="image"></p><p>可以看到输入的用户名已经被我在发送请求之前改掉了(还可以看到我输入的那一串密码123456,衰。。。),修改完毕点一下<strong>Run to Completion</strong>这个请求就完美的发送出去了。</p><p>哈哈,学会了这招,赶紧做点什么去吧。。。</p><p>如果你不想每次请求之前都中断,那你可以:Rules-> Automatic Breakpoint ->Disabled。</p><h2 id="发起请求">发起请求</h2><p>发起请求只需要选择右边的:<strong>Composer</strong>,可以设置请求类型、请求头部和请求参数等等,这里我就不细说了。</p><h2 id="查看响应">查看响应</h2><p>查看响应你只需要选中一条请求,然后选择:Inspectors,上面是请求相关的,下面就是响应相关的。</p><h2 id="修改响应">修改响应</h2><p>修改响应可以像修改请求类似:Rules-> Automatic Breakpoint ->After Reponses,这时会中断响应,然后从<strong>Run to Completion</strong>旁边的**Choose Response...**选择响应文件即可。</p><p>也可以在<strong>AutoResponsder</strong>添加匹配规则,支持正则的哦,然后勾选上<strong>Enable automatic responses</strong>和<strong>Unmated requests passthrough</strong>,这样匹配到相应的Url规则便会自动修改响应,没有匹配到就直接跳过。如图我给知乎换了个logo。</p><p><img src="http://blog.u.qiniudn.com/uploads/fiddler5.png" alt="image"></p><h2 id="远程代理">远程代理</h2><p>远程代理可以用来代理查看其它设备的网络请求。开启方法很简单,选择菜单栏:Tools-> Fiddler Optios,然后选择Connections:</p><p><img src="http://blog.u.qiniudn.com/uploads/fiddler6.png" alt="image"></p><p>勾选上<strong>Allow remote computers connect</strong>,并记下监听端口:8888,获取Fiddler所在机器的IP,我的是:10.15.1.21。</p><p>然后打开手机或者其它移动设备,并确保手机和Fiddler所在机器在同一局域网内,点击你手机所连接的Wifi,在代理那里选择<strong>手动</strong>,并填入主机名:10.15.1.21,端口:8888:</p><p><img src="http://blog.u.qiniudn.com/uploads/fiddler7.png" alt="image"></p><p>使用完后记得把代理取消掉,不然你可能连接这个wifi就无法上网咯。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2015/03/%E6%8A%93%E5%8C%85%E7%A5%9E%E5%99%A8Fiddler/#disqus_thread</comments>
</item>
<item>
<title>ajax跨域问题解决方案</title>
<link>https://www.noonme.com/post/2015/01/ajax%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/</link>
<guid>https://www.noonme.com/post/2015/01/ajax%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/</guid>
<pubDate>Tue, 13 Jan 2015 03:54:21 GMT</pubDate>
<description>
<p>ajax跨域一直是个巨坑的问题。特别是在项目开发过程中,前后端接口调试的时候,一个跨域的问题可能会折腾后端很久,今天就来简单讲讲如何解决这个跨域。</p>
</description>
<content:encoded><![CDATA[<p>ajax跨域一直是个巨坑的问题。特别是在项目开发过程中,前后端接口调试的时候,一个跨域的问题可能会折腾后端很久,今天就来简单讲讲如何解决这个跨域。</p><a id="more"></a><h2 id="通过chrome启动参数">通过chrome启动参数</h2><p>这是最近发现的一种方法,方便快捷有效:在chrome启动里面加参数。</p><p>右键点击chrome快捷方式,在目标那一栏的最后添加<code>--disable-web-security</code>:</p><p><img src="http://blog.u.qiniudn.com/uploads/chromeajax.png" alt></p><p>如果你是MAC系统的话,你可以在你的home目录下新建一个<code>chrome.sh</code>:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line">open -a <span class="string">"Google Chrome"</span> --args --<span class="built_in">disable</span>-web-security</span><br></pre></td></tr></table></figure><p></p><p>并且赋予可执行权限:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo chmod +x chrome.sh</span><br></pre></td></tr></table></figure><p></p><p>需要跨域的时候就执行这个sh来启动chrome。</p><p>通过这个快捷方式来启动chrome成功之后,在上方会有一个黄条提示:</p><blockquote><p>您使用的是不受支持的命令行标记:--disable-web-security。稳定性和安全性会有所下降。</p></blockquote><p>表明你已经成功启动了。这样启动的ajax请求都可以跨域。</p><p><strong>在以这种方式启动前,请先关闭chrome,不然无法以这种方式启动</strong></p><p>这是最简单的方式,只限调试使用。</p><h2 id="通过设置response-header">通过设置Response header</h2><p>通过这种方式需要服务端在响应头部设置允许跨域。这是H5引入的规范,所以不是所有的浏览器都支持的。</p><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Access-Control-Allow-Origin: <origin> | *</span><br></pre></td></tr></table></figure><p></p><p>如:</p><p></p><figure class="highlight java"><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">// java</span></span><br><span class="line">Response.AddHeader(<span class="string">"Access-Control-Allow-Origin"</span>, <span class="string">"http://w3cboy.com"</span>);</span><br></pre></td></tr></table></figure><p></p><p></p><figure class="highlight php"><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">// php</span></span><br><span class="line">header(<span class="string">"Access-Control-Allow-Origin: http://w3cboy.com"</span>);</span><br></pre></td></tr></table></figure><p></p><p>允许所有可以用通配符<code>*</code>,但是这样的话跨域请求不能带cookie(<code>withCredentials</code>)</p><p>有时候需要设置多个域名,所以可以通过服务端来判断加不同的origin:</p><p></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// php</span></span><br><span class="line"><span class="keyword">if</span>( preg_match(<span class="string">"/http:\/\/(.*?)\.w3cboy.com/"</span>, $_SERVER[<span class="string">'HTTP_ORIGIN'</span>], $matches )) {</span><br><span class="line"> $theMatch = $matches[<span class="number">0</span>];</span><br><span class="line"> header(<span class="string">'Access-Control-Allow-Origin: '</span> . $theMatch);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><p>当然也可以在服务器里面设置。</p><p>关于跨域资源共享(Cross-Origin Resource Sharing)的这种方式,还有更多的设置项,可以去<a href="http://www.w3.org/TR/cors/" target="_blank" rel="noopener">W3C</a>看。</p><h2 id="jsonp">JSONP</h2><p>jsonp是通过js的方式来实现跨域数据共享的,因为请求js文件是没有跨域限制的,所以服务端把数据封装成一个js对象返回,请求的时候需要告知一个函数名,一般是放在请求的callback这个参数里面,如通过jquery的ajax,dataType设置为jsonp的话会发现请求会自动加一个callback参数,如<code>&callback=?</code>,那么服务端返回的是:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?({<span class="attr">a</span>:<span class="number">11</span>, <span class="attr">b</span>: <span class="string">"www"</span>});</span><br></pre></td></tr></table></figure><p></p><p>那么在客户端的话是这样的:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> ?(<span class="params">data</span>)</span>{</span><br><span class="line"><span class="built_in">console</span>.log(data);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p></p><p>到这里你也许明白了jsonp的优缺点:能兼容低版本浏览器,但是由于通过get方式请求,所以承载信息量有限。</p><h2 id="document-domain-iframe">document.domain+iframe</h2><p>对于主域相同而子域不同的情况,可以通过设置<code>document.domain</code>的办法来解决。</p><p>具体的做法是可以在<code>http://www.w3cboy.com/a.html</code>和<code>http://github.w3cboy.com/b.html</code>两个文件中分别加上<code>document.domain = 'w3cboy.com'</code>。</p><p>后通过a.html文件中创建一个iframe,去控制iframe的<code>contentDocument</code>,这样两个js文件之间就可以交互了。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//http://www.w3cboy.com/a.html</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">document</span>.domain = <span class="string">'w3cboy.com'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> ifr = <span class="built_in">document</span>.createElement(<span class="string">'iframe'</span>);</span><br><span class="line">ifr.src = <span class="string">'http://github.w3cboy.com/b.html'</span>;</span><br><span class="line">ifr.style.display = <span class="string">'none'</span>;</span><br><span class="line"><span class="built_in">document</span>.body.appendChild(ifr);</span><br><span class="line">ifr.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> x = ifr.contentDocument;</span><br><span class="line"> <span class="built_in">console</span>.log(x.body.nodeValue);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//http://github.w3cboy.com/b.html</span></span><br><span class="line"><span class="built_in">document</span>.domain = <span class="string">'w3cboy.com'</span>;</span><br></pre></td></tr></table></figure><p></p><h2 id="其它方案">其它方案</h2><p>其它方案还有服务代理(在web服务器上封装第三方服务,然后给自己同源的web页面调用),HTML5的<code>postMessage</code>等。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2015/01/ajax%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/#disqus_thread</comments>
</item>
<item>
<title>使用cocos2d-js开发h5游戏</title>
<link>https://www.noonme.com/post/2014/12/%E4%BD%BF%E7%94%A8cocos2d-js%E5%BC%80%E5%8F%91h5%E6%B8%B8%E6%88%8F/</link>
<guid>https://www.noonme.com/post/2014/12/%E4%BD%BF%E7%94%A8cocos2d-js%E5%BC%80%E5%8F%91h5%E6%B8%B8%E6%88%8F/</guid>
<pubDate>Tue, 16 Dec 2014 06:09:19 GMT</pubDate>
<description>
<p>最近一段时间,折腾了一下cocos2d-js,走过,坑过,跨过了一部分,还在加班填剩下的部分。好在今天终于抽出了点时间,因此便想要总结一下。</p>
</description>
<content:encoded><![CDATA[<p>最近一段时间,折腾了一下cocos2d-js,走过,坑过,跨过了一部分,还在加班填剩下的部分。好在今天终于抽出了点时间,因此便想要总结一下。</p><a id="more"></a><p>开始之前你可以先用微信扫一扫玩下我们做的游戏:</p><p><img src="http://7xp0ue.dl1.z0.glb.clouddn.com/2016-08-16_1471327305.png" alt="煤屎军团"></p><p>整个代码已经托管在github:<a href="https://github.com/huanz/msjt" target="_blank" rel="noopener">https://github.com/huanz/msjt</a></p><h2 id="如何学习">如何学习</h2><p>如何开始学习cocos2d-js?我我觉得比较好的方式是:</p><ol><li>看测试例:<a href="http://cocos2d-x.org/js-tests/" target="_blank" rel="noopener">测试例</a></li><li>看Api文档:a.<a href="http://www.cocos2d-x.org/reference/html5-js/V3.0/index.html" target="_blank" rel="noopener">在线API索引</a> b.<a href="http://www.cocos2d-x.org/filedown/Cocos2d-JS-v3.2-RC0-API.zip" target="_blank" rel="noopener">下载版API索引</a></li><li>看源码</li></ol><p>另外还有大神录制了进阶视频教程:<a href="http://cn.cocos2d-x.org/tutorial/lists?id=114" target="_blank" rel="noopener">Cocos2d-JS进阶视频教程</a>,里面关于自学的艺术还是讲的挺好的,在这里我就不累赘了。</p><h2 id="如何开始">如何开始</h2><p>如何开始很简单了,就两条:</p><ol><li>搭环境</li><li>编码</li></ol><p>如果你是做纯web的游戏,搭建环境很简单:</p><ol><li>下载引擎包</li><li>下载<a href="https://www.python.org/download/releases/2.7.6/" target="_blank" rel="noopener">Python2.7.6</a>,<a href="http://mirrors.cnnic.cn/apache//ant/binaries/apache-ant-1.9.4-bin.zip" target="_blank" rel="noopener">Ant</a>,如果没有安装java,请去下载**<a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank" rel="noopener">jdk</a>至少1.7的版本**</li><li>配环境:无疑就是配置java环境、python环境、ant环境变量。基本上就是在系统环境变量path加入相关的bin目录地址。不会的自行google吧</li><li>创建项目:<code>cocos new -l js ProjectName</code></li><li>开始编码:sublime、webstorm等等用你喜欢的开发工具吧。</li></ol><p>官网也有相关文章:<a href="http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/cocos2d-js/2-working-environment-and-workflow/2-2-cross-native-browser-game-with-cocos-console/zh.md" target="_blank" rel="noopener">用Cocos Console工作流开发网页/原生平台游戏(JSB开发环境简介)</a></p><h2 id="如何编码">如何编码</h2><p>如何编码?如果团队协作,遵循团队的编码规范咯。这里我只是简单说一点点:</p><ol><li><p>关于<code>project.json</code>文件的配置信息</p><p>这个其实在main.js里面有注释说明</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">debugMode: <span class="number">0</span></span><br><span class="line"><span class="comment">//0 不显示任何错误信息</span></span><br><span class="line"><span class="comment">//1 显示cc.assert, cc.warn, cc.log</span></span><br><span class="line"></span><br><span class="line">showFPS : <span class="literal">true</span></span><br><span class="line"><span class="comment">//为真会在屏幕左下方显示游戏帧率</span></span><br><span class="line"></span><br><span class="line">id: gameCanvas</span><br><span class="line"><span class="comment">//游戏画布的id</span></span><br><span class="line"></span><br><span class="line">renderMode: <span class="number">0</span></span><br><span class="line"><span class="comment">// 0 : 自动选择渲染引擎:webGl、canvas</span></span><br><span class="line"><span class="comment">// 1 : canvas</span></span><br><span class="line"><span class="comment">// 2 : WebGL(在webgl模式下drawNode会存在严重的锯齿)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// modules : 游戏引用的模块</span></span><br><span class="line"><span class="comment">// 模块的名字可以在frameworks/cocos2d-html5/moduleConfig.json</span></span><br><span class="line"><span class="comment">// 里面找到,所以后期在上线的时候可以选择自己用到的模块引入来减少js文件的大小</span></span><br></pre></td></tr></table></figure><p></p></li></ol><ol start="2"><li><p>关于CCBoot.js</p><p><code>CCBoot.js</code>是入口js文件,所以你很有必要认真看一看其中的代码,比如其中提供一些可用的工具:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建元素</span></span><br><span class="line">cc.newElement</span><br><span class="line"><span class="comment">//监听事件</span></span><br><span class="line">cc._addEventListener</span><br><span class="line"><span class="comment">//循环操作</span></span><br><span class="line">cc.each</span><br><span class="line"><span class="comment">//继承</span></span><br><span class="line">cc.extend</span><br><span class="line"></span><br><span class="line">cc.isFunction</span><br><span class="line">cc.isNumber</span><br><span class="line">cc.isString</span><br><span class="line">cc.isArray</span><br><span class="line">cc.isUndefined</span><br><span class="line">cc.isObject</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="comment">//原生的渲染引擎:CanvasRenderingContext2D/WebGLRenderingContext</span></span><br><span class="line">cc._renderContext</span><br><span class="line"></span><br><span class="line"><span class="comment">//包裹游戏画布的外层div</span></span><br><span class="line">cc._gameDiv / cc.container</span><br></pre></td></tr></table></figure><p></p><p>比如一些你可能想要修改的东西:</p><p>包裹游戏的外层div的id名字:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//大约CCBoot.js的1864行:</span></span><br><span class="line">localContainer.setAttribute(<span class="string">'id'</span>, <span class="string">'Cocos2dGameContainer'</span>);</span><br></pre></td></tr></table></figure><p></p><p>初始加载的时候的画布颜色:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//大约CCBoot.js的641行: </span></span><br><span class="line">canvasNode.style.backgroundColor = <span class="string">"black"</span>;</span><br></pre></td></tr></table></figure><p></p><p>在<code>frameworks/cocos2d-html5/core/platform/</code>目录下也有很多东西,主要是平台相关的东西。</p><p>如简单操作dom的工具<code>miniFramework.js</code>里面的:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cc.$</span><br></pre></td></tr></table></figure><p></p><p>屏幕适配相关的东西'CCEGLView.js'里面的:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">cc.view.adjustViewPort</span><br><span class="line">cc.view.setDesignResolutionSize</span><br><span class="line">cc.view.resizeWithBrowserSize</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p></p><p>文件加载相关的CCLoader.js:</p><p>cocos2d-js的文件加载是通过文件后缀的,如后缀名为<code>["png", "jpg", "bmp","jpeg","gif", "ico"]</code>的文件会通过<code>cc._imgLoader</code>来加载,理解了这里可以用于后期自定义文件加载插件。</p></li></ol><p>还有一些编码上面的东西放在下面再说。</p><h2 id="关于音频">关于音频</h2><p>音频,在移动端上一直是个巨坑的问题。能自动播放,不能自动播放、不能循环播放、根本就不播放等等简直就是处处都是坑啊。</p><p>开始游戏中的音频用的是cocos里面的封装的音频函数,会对音频文件进行预加载、预处理。但是这里有个严重的问题,那就是加载的时候很容易卡在音频那里,而且循环播放也有问题。于是决定用会原生的audio标签。</p><pre><code><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">audio</span> <span class="attr">id</span>=<span class="string">"gameAudio"</span> <span class="attr">src</span>=<span class="string">"res/gameMusic.mp3"</span> <span class="attr">loop</span>=<span class="string">"loop"</span>></span><span class="tag"></<span class="name">audio</span>></span></span><br></pre></td></tr></table></figure></code></pre><p>看起来应该是不错的样子,但是当我们在iphone4s微信内置浏览器里面测试的时候发现这个audio标签居然占据页面空间(不是说加了controls这个属性才会占空间么,坑啊!),于是自然而然想到的解决方案就是none掉:</p><pre><code><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">audio</span> <span class="attr">style</span>=<span class="string">"display:none"</span> <span class="attr">id</span>=<span class="string">"gameAudio"</span> <span class="attr">src</span>=<span class="string">"res/gameMusic.mp3"</span> <span class="attr">loop</span>=<span class="string">"loop"</span>></span><span class="tag"></<span class="name">audio</span>></span></span><br></pre></td></tr></table></figure></code></pre><p>感觉好像轻易解决了这个问题,于是开始编写一堆音频处理的代码。编写完毕之后,拿起iphone试一试,我去啊iphone上音频不能循环播放了啊,能不能再坑点!这个问题困扰了好久不知道什么原因,拿着代码去找师傅去吧!师傅只有一句话:ios上面音频如果不显示或者在屏幕之外有可能就会出问题哦!终于把display:none去掉一切又好了。孩子你还是太年轻了啊!对于占据空间的问题我们现在只能把音频标签作为body最后一个child节点,依然没有很好的解决方案。</p><p>对于音频的自动播放,目前只发现在iphone6 plus是可以的(iphone 6没有所以没试过),所以只能通过用户触摸事件来加载。如果在HTML标记中使用了autoplay属性,将会忽略这个属性,并且不会在加载页面时播放此文件,对于 preload 属性,同样会忽略。唯一能解决的就是用户进入页面是,让用户触发 touch 事件,并且只能在touch事件的回调里面加载才有效。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> $audio = <span class="built_in">document</span>.getElementById(<span class="string">'gameAudio'</span>);</span><br><span class="line"><span class="comment">// 用户点击屏幕的任何一个地方就去加载音频</span></span><br><span class="line"><span class="built_in">document</span>.addEventListener(<span class="string">'touchstart'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>{</span><br><span class="line">$audio.load();</span><br><span class="line">},<span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在某个cc.MenuItemImage/cc.MenuItemSprite/onTouchBegan的回调里面执行播放操作</span></span><br><span class="line"><span class="keyword">try</span>{</span><br><span class="line">$audio.play();</span><br><span class="line">}<span class="keyword">catch</span>(e){}</span><br></pre></td></tr></table></figure><p></p><p>音频的暂停:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>{</span><br><span class="line"> $audio.pause();</span><br><span class="line">}<span class="keyword">catch</span>(e){}</span><br></pre></td></tr></table></figure><p></p><p>音频的停止:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>{</span><br><span class="line"> $audio.currentTime = <span class="number">0</span>;</span><br><span class="line"> $audio.pause();</span><br><span class="line">}<span class="keyword">catch</span>(e){}</span><br></pre></td></tr></table></figure><p></p><p>如果是多个音频文件,可以使用音频audio sprite,所有的音频综合到一个单音频流中,然后播放此流的各个部分。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> audioData = {</span><br><span class="line"> bg: {</span><br><span class="line"> start: <span class="number">0</span>,</span><br><span class="line"> length: <span class="number">1</span></span><br><span class="line"> },</span><br><span class="line"> run: {</span><br><span class="line"> start: <span class="number">1.3</span>,</span><br><span class="line"> length: <span class="number">1.5</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p></p><p>要播放bg这段声音:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>{</span><br><span class="line">$audio.currentTime = audioData.bg.start;</span><br><span class="line">$audio.play();</span><br><span class="line">}<span class="keyword">catch</span>(e){}</span><br></pre></td></tr></table></figure><p></p><p>当播放结束时:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$audio.addEventListener(<span class="string">'timeupdate'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">this</span>.currentTime >= audioData.bg.start + audioData.bg.length) {</span><br><span class="line"> <span class="keyword">this</span>.pause();</span><br><span class="line"> }</span><br><span class="line">}, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure><p></p><p>需要注意的是,更改 currentTime 并不是百分百正确的。假设 currentTime 设为 3.2,而实际得到的却是 3.4。每个 audio sprite 之间需要少量的空间,以避免寻找到另一个 sprite 的头部。</p><h2 id="一些工具">一些工具</h2><p><strong>精灵图制作:</strong><a href="https://www.codeandweb.com/texturepacker" target="_blank" rel="noopener">TexturePacker</a></p><p>相当好用的精灵图制作工具,虽然收费,但是可以申请免费的密钥,具体申请地址自己查吧。</p><p>用它制作精灵图的时候注意一个地方就可以了:</p><p><img src="http://blog.u.qiniudn.com/uploads/TexturePacke.png" alt></p><p>勾上这个Reduce border artifacts,它会让你的精灵图边缘没有锯齿,其它的配置地方我基本上都是默认的。</p><p><strong>位图字体制作:</strong><a href="http://www.angelcode.com/products/bmfont/" target="_blank" rel="noopener">Bitmap Font Generator</a></p><p>位图字体其实是一张图片,然后记录每个文字的位置和大小,因此字体的大小在生成的时候已经定义了,所以在用的时候最好不要改变字体的大小,如果改变了大小会对图片进行缩放,可能会出现锯齿。</p><p>用<a href="http://www.angelcode.com/products/bmfont/" target="_blank" rel="noopener">Bitmap Font Generator</a>这个软件制作的字体会生成一个png文件和一个fnt文件。选择左上方的<code>Options->Font settings</code>:</p><p><img src="http://blog.u.qiniudn.com/uploads/bmfont.png" alt></p><p>设置字体格式和字体大小。其它基本上都是默认设置就好了。</p><p>可以在主界面上面一个个点击选择需要制作的文字,也可以把要选择的字体放在一个txt文件里面(<strong>txt文件编码为Utf-8</strong>),然后选择菜单栏的<code>Edit->Select chars from file</code>来选择要制作的文字。</p><p>Bitmap Font Generator还支持从image文件生成字体纹理图,选择菜单栏的<code>Edit->Open Image Manager->Image->Import image</code>(注意路径里面不要有中文)</p><p><img src="http://blog.u.qiniudn.com/uploads/bmfonti.png" alt></p><p>Id那里填写对应文字的ASCII码,如我上传的图片里面的文字是2,其对应的ASCII码是50。如果不知道某个文字的ASCII码,可以去这里查:<a href="http://ascii.911cha.com/" target="_blank" rel="noopener">ASCII码对照表</a>。</p><p>选择好之后点击菜单栏<code>Options->Export options</code>:</p><p><img src="http://blog.u.qiniudn.com/uploads/bmfonte.png" alt></p><p>Padding那里设置每个文字的间距,如果你需要对文字有阴影什么的话,那你最好设置一下padding值,四个值分别是top、right、bottom、left的间距值。</p><p>Texture那里设置纹理图的大小,如果设置的太小,文字多的话他可能会生成多个纹理图,所以我建议设置宽高最好让文字能够放在一张图里面比较合适。</p><p>Bit depth设置色深,如果为了效果好点可以选择32,对效果要求没那么细腻,想要生成的文件小点可以选择8。</p><p>Presets那里设置生成的纹理图的样式,我一般选择是白色文字加上透明背景,即<code>White text with alpha</code></p><p>Textures生成纹理图的格式,肯定选择png了。</p><p><strong>字体精简:</strong><a href="http://www.high-logic.com/font-editor/fontcreator.html" target="_blank" rel="noopener">FontCreator</a></p><p>为什么需要加载字体?因为有时候页面上会有不同大小的特殊字体,用位图又比较麻烦,加载整个字体库又太大了,于是我找到了这个软件。</p><p>选择菜单栏<code>File->New Project</code>,填写你的字体名字和字体样式。然后选择菜单栏<code>File->Open</code>,打开你要精简的字体。(在新建项目的时候有一个Include outlines/Don'n include outlines的选项。如果选择Include outlines它会默认帮你添加一些字符)</p><p><img src="http://blog.u.qiniudn.com/uploads/fontc.png" alt></p><p>如果是可以直接看得到的字符,我可以直接在原有的字体文件中选中这个字符,然后复制粘贴到我的项目里面对应的位置上去,如果不是的话,我们可以通过下面的方法添加进去。</p><p>ctrl+f键查找某个字符,如我们要添加的字是“我”,然后右键选择<code>Glyph Properties</code></p><p><img src="http://blog.u.qiniudn.com/uploads/fontc1.png?" alt></p><p>复制<code>Codepoints</code>里面的参数,然后选中自己的项目,选择菜单栏<code>Insert->Charaacters</code>,把刚才复制的Codepoints粘贴到对应的位置。</p><p><img src="http://blog.u.qiniudn.com/uploads/fontc2.png" alt></p><p>这时你会发现在你项目字符的最后会添加一个灰色方块,回到原始字体那里,选中那个字直接复制粘贴到刚才添加的那个方块里面就可以了:</p><p><img src="http://blog.u.qiniudn.com/uploads/fontc3.jpg" alt></p><p>在上面的图里面你可以看到有很多灰色的方块,看看里面显示的字符,如果你用不到你可以直接点Delete键把它删掉。</p><p>除了用这个软件外,最近发现一个工具也可以用来精简压缩字体,感兴趣的可以试试:<a href="http://font-spider.org/" target="_blank" rel="noopener">字蛛——中文字体自动化压缩工具</a></p><p><strong>地图制作:</strong><a href="http://www.mapeditor.org/" target="_blank" rel="noopener">Tiled Map Editor</a></p><p>这个其实在我们的项目中没有用到,所以在这里提一下而已,有用到的可以自己去琢磨一下。</p><h2 id="项目发布">项目发布</h2><p>项目发布意味着什么?简单来说就意味着资源压缩、代码合并。首先来说下资源压缩吧。</p><p><strong>让你爱恨交加的closure compiler</strong></p><p>不是说cocos2d-js么?怎么会扯到closure compiler。如果你想要发布你的cocos2d游戏,我觉得就不得不说说closure compiler这玩意,这也是为何安装jdk的时候要求要jdk7以上的版本了。</p><p>在cocos项目发布的时候,有一个命令是<code>cocos compile -p web -m release --advanced</code>,后面加了一个<code>advanced</code>的参数,这种模式下面使用的是closure compiler的高级js压缩模式,压缩比例惊人,同时会优化js的执行,因此压缩之后的js性能也会得到一定提升。但是想要用这个高级压缩模式可是有坑的,很可能你会发现你的代码经过这种压缩模式压缩之后就报错了。</p><p>所以你最好去<a href="https://developers.google.com/closure/compiler/" target="_blank" rel="noopener">closure compiler官网</a>(google出品,所以需要翻墙)看一看,这里我只提简单的几点。</p><p>需要保留变量名的,要么用<code>@expose</code>关键字申明,要么使用<code>[]</code>来引用,如:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** @expose */</span></span><br><span class="line"><span class="built_in">window</span>.MM;</span><br><span class="line"><span class="comment">/** @expose */</span></span><br><span class="line">M.add;</span><br><span class="line"><span class="comment">/** @expose */</span></span><br><span class="line">M.add.Pos;</span><br><span class="line"></span><br><span class="line"><span class="comment">// or</span></span><br><span class="line"><span class="built_in">window</span>[<span class="string">'MM'</span>];</span><br><span class="line">MM[<span class="string">'add'</span>];</span><br><span class="line">MM[<span class="string">'add'</span>][<span class="string">'Pos'</span>];</span><br></pre></td></tr></table></figure><p></p><p>比如在ajax请求数据的时候的请求参数和响应参数,你要写成这样:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// request data</span></span><br><span class="line"><span class="comment">// 加引号</span></span><br><span class="line">{<span class="string">'name'</span>: <span class="string">'dddd'</span>, <span class="string">'id'</span>: <span class="string">'123'</span>}</span><br><span class="line"></span><br><span class="line"><span class="comment">// response data</span></span><br><span class="line"><span class="comment">// 通过数组的方式取值</span></span><br><span class="line">data[<span class="string">'list'</span>];</span><br><span class="line">data[<span class="string">'list'</span>][<span class="string">'userName'</span>]</span><br></pre></td></tr></table></figure><p></p><p>记住,任何你需要让它在压缩之后能保持原样输出的你都应该这样写。如果要了解更多你可以去它官网看看,或者看一看cocos2d-js的源码,里面有很多应对closure compiler高级压缩模式的注解。</p><p><strong>plist文件合并</strong></p><p>一次请求是比较耗费资源的,所以我们把plist文件合并在一个文件里面来加载,同时也对plist进行了压缩,下面是加载解析合并后的plist文件的插件源码:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将plist合并成一个加载</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">cc._pjsonLoader = {</span><br><span class="line"> KEY : {</span><br><span class="line"> frames : <span class="number">0</span>,</span><br><span class="line"> rect : <span class="number">0</span>, <span class="attr">size</span> : <span class="number">1</span>, <span class="attr">offset</span> : <span class="number">2</span>, <span class="attr">rotated</span> : <span class="number">3</span>, <span class="attr">aliases</span> : <span class="number">4</span>,</span><br><span class="line"> meta : <span class="number">1</span>,</span><br><span class="line"> image : <span class="number">0</span></span><br><span class="line"> },</span><br><span class="line"> _parse : <span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> KEY = <span class="keyword">this</span>.KEY;</span><br><span class="line"> <span class="keyword">var</span> frames = {}, meta = data[KEY.meta] ? {<span class="attr">image</span> : data[KEY.meta][KEY.image]} : {};</span><br><span class="line"> <span class="keyword">var</span> tempFrames = data[KEY.frames];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> frameName <span class="keyword">in</span> tempFrames) {</span><br><span class="line"> <span class="keyword">var</span> f = tempFrames[frameName];</span><br><span class="line"> <span class="keyword">var</span> rect = f[KEY.rect];</span><br><span class="line"> <span class="keyword">var</span> size = f[KEY.size];</span><br><span class="line"> <span class="keyword">var</span> offset = f[KEY.offset];</span><br><span class="line"> frames[frameName] = {</span><br><span class="line"> rect : {<span class="attr">x</span> : rect[<span class="number">0</span>], <span class="attr">y</span> : rect[<span class="number">1</span>], <span class="attr">width</span> : rect[<span class="number">2</span>], <span class="attr">height</span> : rect[<span class="number">3</span>]},</span><br><span class="line"> size : {<span class="attr">width</span> : size[<span class="number">0</span>], <span class="attr">height</span> : size[<span class="number">1</span>]},</span><br><span class="line"> offset : {<span class="attr">x</span> : offset[<span class="number">0</span>], <span class="attr">y</span> : offset[<span class="number">1</span>]},</span><br><span class="line"> rotated : f[KEY.rotated],</span><br><span class="line"> aliases : f[KEY.aliases]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> {<span class="attr">_inited</span> : <span class="literal">true</span>, <span class="attr">frames</span> : frames, <span class="attr">meta</span> : meta};</span><br><span class="line"> },</span><br><span class="line"> load : <span class="function"><span class="keyword">function</span>(<span class="params">realUrl, url, res, cb</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> self = <span class="keyword">this</span>, locLoader = cc.loader, cache = locLoader.cache;</span><br><span class="line"> locLoader.loadJson(realUrl, <span class="function"><span class="keyword">function</span>(<span class="params">err, pkg</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span>(err) <span class="keyword">return</span> cb(err);</span><br><span class="line"> <span class="keyword">var</span> dir = cc.path.dirname(url);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> pkg) {</span><br><span class="line"> <span class="keyword">var</span> filePath = cc.path.join(dir, key);</span><br><span class="line"> cache[filePath] = self._parse(pkg[key]);</span><br><span class="line"> }</span><br><span class="line"> cb(<span class="literal">null</span>, <span class="literal">true</span>);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p></p><pre><code>//注册这个插件,前面说过cocos文件加载是通过文件后缀名来判断使用哪个加载器来加载的,所以这里会加载后缀名为.pjson的文件</code></pre><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cc.loader.register([<span class="string">"pjson"</span>], cc._pjsonLoader);</span><br></pre></td></tr></table></figure><p></p><p>那么如何来生成这个<code>.pjson</code>文件呢?下面是我用<a href="http://nodejs.org/" target="_blank" rel="noopener">nodejs</a>写的一个脚本,可以读取目录下面所有的<code>.plist</code>文件,然后生成一个<code>.pjson</code>文件:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//pjson.js</span></span><br><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"><span class="keyword">var</span> plist = <span class="built_in">require</span>(<span class="string">'plist'</span>);</span><br><span class="line"><span class="keyword">var</span> pjson = {};</span><br><span class="line"></span><br><span class="line"><span class="comment">//var src = "res/*.plist";</span></span><br><span class="line"><span class="comment">//node pjson</span></span><br><span class="line">fs.readdir(<span class="string">'./res'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">err, files</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="keyword">throw</span> err;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = files.length - <span class="number">1</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">var</span> file = files[i],</span><br><span class="line"> ext = file.split(<span class="string">'.'</span>)[<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">if</span>(ext === <span class="string">'plist'</span>){</span><br><span class="line"> pjson[file] = [];</span><br><span class="line"> pjson[file][<span class="number">0</span>] = {};</span><br><span class="line"> <span class="keyword">var</span> data = fs.readFileSync(<span class="string">'./res/'</span>+file, <span class="string">'UTF-8'</span>);</span><br><span class="line"><span class="keyword">var</span> frames = plist.parse(data.toString()).frames;</span><br><span class="line"><span class="keyword">var</span> fileName = <span class="built_in">Object</span>.keys(frames);</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> j=<span class="number">0</span>; j< fileName.length; j++){</span><br><span class="line"><span class="keyword">var</span> dat = pjson[file][<span class="number">0</span>][fileName[j]] = [];</span><br><span class="line"><span class="keyword">var</span> frame = frames[fileName[j]];</span><br><span class="line">dat[<span class="number">0</span>] = frame.frame.replace(<span class="regexp">/{|}/g</span>, <span class="string">''</span>).split(<span class="string">','</span>);</span><br><span class="line">dat[<span class="number">1</span>] = frame.sourceSize.replace(<span class="regexp">/{|}/g</span>, <span class="string">''</span>).split(<span class="string">','</span>);</span><br><span class="line">dat[<span class="number">2</span>] = frame.offset.replace(<span class="regexp">/{|}/g</span>, <span class="string">''</span>).split(<span class="string">','</span>);</span><br><span class="line"> <span class="keyword">if</span>(frame.rotated) dat[<span class="number">3</span>] = <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> fs.writeFile(<span class="string">'./res/plists.pjson'</span>, <span class="built_in">JSON</span>.stringify(pjson), <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'done!'</span>);</span><br><span class="line">});</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>所以你需要在nodejs命令行里面运行:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">// 第一次运行请先安装依赖包</span><br><span class="line">npm install plist</span><br><span class="line"></span><br><span class="line">// 生成pjson文件</span><br><span class="line">node pjson.js</span><br></pre></td></tr></table></figure><p></p><p>那么如何使用这个文件呢?很简单,你只需要在<code>resource.js</code>的<code>g_resources</code>数组里面删除<code>plist</code>相关的,然后添加这个<code>plists.pjson</code>的路径就可以了,其它不用作任何改动。</p><p><strong>图片资源压缩</strong></p><p>关于图片和其他静态资源,我用了一个前端自动化任务工具<code>gulp</code>,不懂的可以看我上篇博文:<a href="http://w3cboy.com/post/2014/10/getting-started-with-gulp/" target="_blank" rel="noopener">Gulp上手</a>,下面是我针对项目配置的<code>gulp</code>任务<code>gulpfile.js</code>:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">'gulp'</span>),</span><br><span class="line"> imagemin = <span class="built_in">require</span>(<span class="string">'gulp-imagemin'</span>),</span><br><span class="line"> pngquant = <span class="built_in">require</span>(<span class="string">'imagemin-pngquant'</span>),</span><br><span class="line"> cache = <span class="built_in">require</span>(<span class="string">'gulp-cache'</span>),</span><br><span class="line"> autoprefixer = <span class="built_in">require</span>(<span class="string">'gulp-autoprefixer'</span>),</span><br><span class="line"> minifycss = <span class="built_in">require</span>(<span class="string">'gulp-minify-css'</span>),</span><br><span class="line"> rev = <span class="built_in">require</span>(<span class="string">'gulp-rev'</span>),</span><br><span class="line"> revReplace = <span class="built_in">require</span>(<span class="string">'gulp-rev-replace'</span>),</span><br><span class="line"> useref = <span class="built_in">require</span>(<span class="string">'gulp-useref'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> basePath = <span class="string">'publish/html5/'</span>;</span><br><span class="line"></span><br><span class="line">gulp.task(<span class="string">'image'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(basePath+<span class="string">'res/**/*.png'</span>)</span><br><span class="line"> .pipe(cache(imagemin({</span><br><span class="line"> optimizationLevel: <span class="number">7</span>,</span><br><span class="line"> use: [pngquant({ <span class="attr">quality</span>: <span class="string">'60-80'</span>, <span class="attr">speed</span>: <span class="number">1</span> })]</span><br><span class="line"> })))</span><br><span class="line"> .pipe(gulp.dest(basePath+<span class="string">'res'</span>));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">gulp.task(<span class="string">'style'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(<span class="string">'css/**/*.css'</span>)</span><br><span class="line"> .pipe(autoprefixer(<span class="string">'Android'</span>, <span class="string">'BlackBerry'</span>, <span class="string">'iOS'</span>, <span class="string">'OperaMobile'</span>, <span class="string">'ChromeAndroid'</span>, <span class="string">'FirefoxAndroid'</span>, <span class="string">'ExplorerMobile'</span>))</span><br><span class="line"> .pipe(minifycss())</span><br><span class="line"> .pipe(gulp.dest(basePath+<span class="string">'css'</span>));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">gulp.task(<span class="string">'static'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> userefAssets = useref.assets();</span><br><span class="line"> <span class="keyword">return</span> gulp.src(basePath+<span class="string">'index.html'</span>)</span><br><span class="line"> .pipe(userefAssets)</span><br><span class="line"> .pipe(rev()) </span><br><span class="line"> .pipe(userefAssets.restore())</span><br><span class="line"> .pipe(useref())</span><br><span class="line"> .pipe(revReplace())</span><br><span class="line"> .pipe(gulp.dest(basePath));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">gulp.task(<span class="string">'default'</span>, [<span class="string">'image'</span>, <span class="string">'style'</span>, <span class="string">'static'</span>]);</span><br></pre></td></tr></table></figure><p></p><p>因为其中用到了对每次生成的js、css文件进行md5命名和合并操作,所以你需要在你的html文件里面作如下配置:</p><p></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></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- build:css css/main.css --></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"css/a.css"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"css/b.css"</span>></span></span><br><span class="line"> <span class="comment"><!-- endbuild --></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- build:js game.js --></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"frameworks/cocos2d-html5/CCBoot.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"main.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="comment"><!-- endbuild --></span></span><br></pre></td></tr></table></figure><p></p><p><strong>整个项目发布</strong></p><p>接着我写了一个整个项目的发布脚本<code>build.js</code>:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> exec = <span class="built_in">require</span>(<span class="string">'child_process'</span>).exec;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> buildProcess = exec(<span class="string">'cocos compile -p web -m release --advanced'</span>, {});</span><br><span class="line">buildProcess.on(<span class="string">'close'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'start gulp task'</span>);</span><br><span class="line"><span class="keyword">var</span> nextProcess = exec(<span class="string">'gulp default'</span>, {});</span><br><span class="line">nextProcess.on(<span class="string">'close'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'gulp task end'</span>);</span><br><span class="line">});</span><br><span class="line">nextProcess.stdout.setEncoding(<span class="string">'utf-8'</span>);</span><br><span class="line">nextProcess.stdout.on(<span class="string">'data'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(data);</span><br><span class="line">});</span><br><span class="line">nextProcess.stderr.setEncoding(<span class="string">'utf-8'</span>);</span><br><span class="line">nextProcess.stderr.on(<span class="string">'data'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(data);</span><br><span class="line">});</span><br><span class="line">});</span><br><span class="line">buildProcess.stdout.setEncoding(<span class="string">'utf-8'</span>);</span><br><span class="line">buildProcess.stdout.on(<span class="string">'data'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(data);</span><br><span class="line">});</span><br><span class="line">buildProcess.stderr.setEncoding(<span class="string">'utf-8'</span>);</span><br><span class="line">buildProcess.stderr.on(<span class="string">'data'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(data);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>所以每次项目发布的时候,你只需要打开nodejs命令行,然后执行一下一个命令就完事:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node build.js</span><br></pre></td></tr></table></figure><p></p><h2 id="一些可能对你有用的代码">一些可能对你有用的代码</h2><p><strong>ajax简单封装</strong></p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line">Utils.ArrayProto = <span class="built_in">Array</span>.prototype;</span><br><span class="line">Utils.slice = Utils.ArrayProto.slice;</span><br><span class="line"></span><br><span class="line">Utils.decode = <span class="built_in">decodeURIComponent</span>;</span><br><span class="line">Utils.encode = <span class="built_in">encodeURIComponent</span>;</span><br><span class="line"></span><br><span class="line">Utils.defaults = <span class="function"><span class="keyword">function</span>(<span class="params">obj</span>)</span>{</span><br><span class="line"> cc.each(Utils.slice.call(<span class="built_in">arguments</span>, <span class="number">1</span>), <span class="function"><span class="keyword">function</span>(<span class="params">o</span>)</span>{</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> k <span class="keyword">in</span> o){</span><br><span class="line"> <span class="keyword">if</span> (obj[k] == <span class="literal">null</span>) obj[k] = o[k];</span><br><span class="line"> } </span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span> obj;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.formData = <span class="function"><span class="keyword">function</span>(<span class="params">o</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> kvps = [], regEx = <span class="regexp">/%20/g</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> k <span class="keyword">in</span> o) kvps.push(Utils.encode(k).replace(regEx, <span class="string">"+"</span>) + <span class="string">"="</span> + Utils.encode(o[k].toString()).replace(regEx, <span class="string">"+"</span>));</span><br><span class="line"> <span class="keyword">return</span> kvps.join(<span class="string">'&'</span>);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.ajax = <span class="function"><span class="keyword">function</span>(<span class="params">o</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> xhr = cc.loader.getXMLHttpRequest();</span><br><span class="line"> o = Utils.defaults(o, {<span class="attr">type</span>: <span class="string">"GET"</span>, <span class="attr">data</span>: <span class="literal">null</span>, <span class="attr">dataType</span>: <span class="string">'json'</span>, <span class="attr">progress</span>: <span class="literal">null</span>, <span class="attr">contentType</span>: <span class="string">"application/x-www-form-urlencoded"</span>});</span><br><span class="line"> <span class="comment">//ajax进度的</span></span><br><span class="line"><span class="comment">//if(o.progress) Utils.Progress.start(o.progress);</span></span><br><span class="line"> xhr.onreadystatechange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (xhr.readyState == <span class="number">4</span>){</span><br><span class="line"> <span class="keyword">if</span> (xhr.status < <span class="number">300</span>){</span><br><span class="line"> <span class="keyword">var</span> res;</span><br><span class="line"> <span class="keyword">if</span>(o.dataType == <span class="string">'json'</span>){</span><br><span class="line"> res = <span class="built_in">window</span>.JSON ? <span class="built_in">window</span>.JSON.parse(xhr.responseText): <span class="built_in">eval</span>(xhr.responseText);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> res = xhr.responseText;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(!!res) o.success(res);</span><br><span class="line"><span class="comment">////ajax进度的</span></span><br><span class="line"> <span class="comment">//if(o.progress) Utils.Progress.done();</span></span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="keyword">if</span>(o.error) o.error(xhr, xhr.status, xhr.statusText); </span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">//是否需要带cookie的跨域</span></span><br><span class="line"><span class="comment">//if("withCredentials" in xhr) xhr.withCredentials = true;</span></span><br><span class="line"> <span class="keyword">var</span> url = o.url, data = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">var</span> isPost = o.type == <span class="string">"POST"</span> || o.type == <span class="string">"PUT"</span>;</span><br><span class="line"> <span class="keyword">if</span>( o.data && <span class="keyword">typeof</span> o.data == <span class="string">'object'</span> ){</span><br><span class="line"> data = Utils.formData(o.data);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!isPost && data) {</span><br><span class="line"> url += <span class="string">"?"</span> + data;</span><br><span class="line"> data = <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> xhr.open(o.type, url, <span class="literal">true</span>);</span><br><span class="line"> <span class="keyword">if</span> (isPost) {</span><br><span class="line"> xhr.setRequestHeader(<span class="string">"Content-Type"</span>, o.contentType);</span><br><span class="line"> }</span><br><span class="line"> xhr.send(data);</span><br><span class="line"> <span class="keyword">return</span> xhr;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.get = <span class="function"><span class="keyword">function</span>(<span class="params">url, data, success</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span>(cc.isFunction(data)){</span><br><span class="line"> success = data;</span><br><span class="line"> data = <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> Utils.ajax({<span class="attr">url</span>: url, <span class="attr">type</span>: <span class="string">"GET"</span>, <span class="attr">data</span>: data, <span class="attr">success</span>: success});</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.post = <span class="function"><span class="keyword">function</span>(<span class="params">url, data, success</span>)</span>{</span><br><span class="line"> <span class="keyword">if</span>(cc.isFunction(data)){</span><br><span class="line"> success = data;</span><br><span class="line"> data = <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> Utils.ajax({<span class="attr">url</span>: url, <span class="attr">type</span>: <span class="string">"POST"</span>, <span class="attr">data</span>: data, <span class="attr">success</span>: success});</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p></p><p>用法和jquery的ajax用法类似</p><p><strong>LoaderScene重定义</strong></p><p>下面是我的loaderScene的一部分代码:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> LoaderScene = cc.Scene.extend({</span><br><span class="line"> _interval : <span class="literal">null</span>,</span><br><span class="line"> _label : <span class="literal">null</span>,</span><br><span class="line"> _className:<span class="string">"LoaderScene"</span>,</span><br><span class="line"> numberSprites: [],</span><br><span class="line"></span><br><span class="line"> init : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> self = <span class="keyword">this</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// bg</span></span><br><span class="line"> <span class="keyword">var</span> bgLayer = self._bgLayer = <span class="keyword">new</span> cc.LayerColor(cc.color(<span class="number">216</span>, <span class="number">216</span>, <span class="number">216</span>));</span><br><span class="line"> bgLayer.setPosition(cc.visibleRect.bottomLeft);</span><br><span class="line"> self.addChild(bgLayer, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//you codes</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> },</span><br><span class="line"> onEnter: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> cc.Node.prototype.onEnter.call(<span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">var</span> loader = (<span class="keyword">this</span>.loadType == <span class="string">'resource'</span>) ? <span class="keyword">this</span>._startLoading : <span class="keyword">this</span>._startAjax;</span><br><span class="line"> <span class="keyword">this</span>.schedule(loader, <span class="number">0.3</span>);</span><br><span class="line"> },</span><br><span class="line"> onExit: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> cc.Node.prototype.onExit.call(<span class="keyword">this</span>);</span><br><span class="line"> <span class="comment">//you codes</span></span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> initWithResources: <span class="function"><span class="keyword">function</span> (<span class="params">resources, cb</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(cc.isString(resources))</span><br><span class="line"> resources = [resources];</span><br><span class="line"> <span class="keyword">this</span>.resources = resources || [];</span><br><span class="line"> <span class="keyword">this</span>.cb = cb;</span><br><span class="line"> <span class="keyword">this</span>.loadType = <span class="string">'resource'</span>;</span><br><span class="line"> },</span><br><span class="line"> _startLoading: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> self = <span class="keyword">this</span>;</span><br><span class="line"> self.unschedule(self._startLoading);</span><br><span class="line"> <span class="keyword">var</span> res = self.resources;</span><br><span class="line"> cc.loader.load(res, self._loadProgress.bind(self), <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (self.cb) self.cb();</span><br><span class="line"> });</span><br><span class="line"> },</span><br><span class="line"> initAjax: <span class="function"><span class="keyword">function</span>(<span class="params">ajaxSetting</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.ajaxSetting = ajaxSetting;</span><br><span class="line"> <span class="keyword">this</span>.loadType = <span class="string">'ajax'</span>;</span><br><span class="line"> },</span><br><span class="line"> _startAjax: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> self = <span class="keyword">this</span>,</span><br><span class="line"> preNum = <span class="number">0</span>;</span><br><span class="line"> self.unschedule(self._startAjax);</span><br><span class="line"> <span class="keyword">if</span>(!<span class="keyword">this</span>.ajaxSetting.progress){</span><br><span class="line"> <span class="keyword">this</span>.ajaxSetting.progress = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>)</span>{</span><br><span class="line"> n = n.toFixed(<span class="number">2</span>);</span><br><span class="line"> <span class="keyword">if</span>(preNum === n) <span class="keyword">return</span>;</span><br><span class="line"> preNum = n;</span><br><span class="line"> self._loadProgress(<span class="literal">null</span>, <span class="number">1</span>, n);</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line"> Utils.ajax(<span class="keyword">this</span>.ajaxSetting);</span><br><span class="line"> },</span><br><span class="line"> _loadProgress: <span class="function"><span class="keyword">function</span>(<span class="params">result, count, loadedCount</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> percent = (loadedCount / count * <span class="number">100</span>) | <span class="number">0</span>;</span><br><span class="line"> percent = <span class="built_in">Math</span>.min(percent, <span class="number">100</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(percent);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">LoaderScene.preload = <span class="function"><span class="keyword">function</span>(<span class="params">resources, cb</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> _cc = cc;</span><br><span class="line"> <span class="keyword">if</span>(!_cc.loaderScene) {</span><br><span class="line"> _cc.loaderScene = <span class="keyword">new</span> LoaderScene();</span><br><span class="line"> _cc.loaderScene.init();</span><br><span class="line"> }</span><br><span class="line"> _cc.loaderScene.initWithResources(resources, cb);</span><br><span class="line"> cc.director.runScene(_cc.loaderScene);</span><br><span class="line"> <span class="keyword">return</span> _cc.loaderScene;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">LoaderScene.ajaxLoad = <span class="function"><span class="keyword">function</span>(<span class="params">ajaxSetting</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> _cc = cc;</span><br><span class="line"> <span class="keyword">if</span>(!_cc.loaderScene) {</span><br><span class="line"> _cc.loaderScene = <span class="keyword">new</span> LoaderScene();</span><br><span class="line"> _cc.loaderScene.init();</span><br><span class="line"> }</span><br><span class="line"> _cc.loaderScene.initAjax(ajaxSetting);</span><br><span class="line"> cc.director.runScene(_cc.loaderScene);</span><br><span class="line"> <span class="keyword">return</span> _cc.loaderScene;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p></p><p>如果是普通的资源加载调用<code>LoaderScene.preload</code>,如果是ajax加载定调用<code>LoaderScene.ajaxLoad</code>,配合上面的ajax封装来用:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">LoaderScene.preload(g_resources, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'loaded!!!'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">LoaderScene.ajaxLoad({</span><br><span class="line">url: <span class="string">'test.json'</span>,</span><br><span class="line">data: {<span class="string">'name'</span>: <span class="string">'ddd'</span>}</span><br><span class="line">success: <span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>{</span><br><span class="line"><span class="built_in">console</span>.log(data);</span><br><span class="line">},</span><br><span class="line">error: <span class="function"><span class="keyword">function</span>(<span class="params">xhr, status, msg</span>)</span>{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>其中有个显示ajax加载百分比的方法,需要用到一个生成加载百分比的方法,同时要把ajax封装里面的<code>if(o.progress)</code>的注释打开:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line">Utils.noop = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{};</span><br><span class="line"></span><br><span class="line">Utils.clamp = <span class="function"><span class="keyword">function</span>(<span class="params">n, min, max</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (n < min) <span class="keyword">return</span> min;</span><br><span class="line"> <span class="keyword">if</span> (n > max) <span class="keyword">return</span> max;</span><br><span class="line"> <span class="keyword">return</span> n;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.Progress = {};</span><br><span class="line"><span class="comment">//设置最小值、更新频率和速度</span></span><br><span class="line">Utils.Progress.settings = {</span><br><span class="line"> minimum: <span class="number">0.1</span>,</span><br><span class="line"> trickle: <span class="literal">true</span>,</span><br><span class="line"> trickleRate: <span class="number">0.3</span>,</span><br><span class="line"> trickleSpeed: <span class="number">100</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.Progress.status = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line">Utils.Progress.set = <span class="function"><span class="keyword">function</span>(<span class="params">n</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> progress = Utils.Progress;</span><br><span class="line"> n = Utils.clamp (n, progress.settings.minimum, <span class="number">1</span>);</span><br><span class="line"> progress.status = n;</span><br><span class="line"> progress.cb(progress.status);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.Progress.inc = <span class="function"><span class="keyword">function</span>(<span class="params">amount</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> progress = Utils.Progress,</span><br><span class="line"> n = progress.status;</span><br><span class="line"> <span class="keyword">if</span> (!n) {</span><br><span class="line"> <span class="keyword">return</span> progress.start();</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> amount = (<span class="number">1</span> - n) * Utils.clamp(<span class="built_in">Math</span>.random() * n, <span class="number">0.1</span>, <span class="number">0.95</span>);</span><br><span class="line"> n = Utils.clamp(n + amount, <span class="number">0</span>, <span class="number">0.994</span>);</span><br><span class="line"> <span class="keyword">return</span> progress.set(n);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.Progress.trickle = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> progress = Utils.Progress;</span><br><span class="line"> <span class="keyword">return</span> progress.inc(<span class="built_in">Math</span>.random() * progress.settings.trickleRate);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.Progress.start = <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> progress = Utils.Progress;</span><br><span class="line"> progress.cb = cb || Utils.noop;</span><br><span class="line"> <span class="keyword">if</span> (!progress.status) progress.set(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> timer = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (progress.status === <span class="number">1</span>) {</span><br><span class="line"> clearTimeout(timer);</span><br><span class="line"> timer = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> progress.trickle();</span><br><span class="line"> work();</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> work = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> setTimeout(timer, progress.settings.trickleSpeed);</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">if</span> (progress.settings.trickle) work();</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">Utils.Progress.done = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> progress = Utils.Progress;</span><br><span class="line"> <span class="keyword">return</span> progress.inc(<span class="number">0.3</span> + <span class="number">0.5</span> * <span class="built_in">Math</span>.random()).set(<span class="number">1</span>);</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p></p><p><strong>LoaderLayer的定义</strong></p><p>LoaderLayer是在加载的时候在当前场景上面添加一个遮罩层,这里主要是ajax加载的,代码如下:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line">Config.winSize = cc.size(<span class="number">720</span>, <span class="number">1134</span>);</span><br><span class="line">Config.w = Config.winSize.width;</span><br><span class="line">Config.h = Config.winSize.height;</span><br><span class="line">Config.w_2 = Config.w / <span class="number">2</span>;</span><br><span class="line">Config.h_2 = Config.h / <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> LoaderLayer = cc.LayerColor.extend({</span><br><span class="line">count: <span class="number">0</span>,</span><br><span class="line"> ctor:<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>._super(cc.color(<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">160</span>));</span><br><span class="line"> <span class="keyword">var</span> draw = <span class="keyword">new</span> cc.DrawNode();</span><br><span class="line"> draw.x = Config.w_2 - <span class="number">50</span>;</span><br><span class="line"> draw.y = <span class="number">260</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>; i<<span class="number">3</span>; i++){</span><br><span class="line"> draw.drawRect(cc.p(<span class="number">40</span>*i, <span class="number">0</span>), cc.p(<span class="number">40</span>*i+<span class="number">20</span>, <span class="number">20</span>), cc.color(<span class="number">181</span>, <span class="number">181</span>, <span class="number">181</span>), <span class="number">1</span>, cc.color(<span class="number">181</span>, <span class="number">181</span>, <span class="number">181</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> draw2 = <span class="keyword">this</span>.draw = <span class="keyword">new</span> cc.DrawNode();</span><br><span class="line"> draw2.x = Config.w_2 - <span class="number">50</span>;</span><br><span class="line"> draw2.y = <span class="number">260</span>;</span><br><span class="line"> draw2.drawRect(cc.p(<span class="number">0</span>, <span class="number">0</span>), cc.p(<span class="number">20</span>, <span class="number">20</span>), cc.color(<span class="number">239</span>, <span class="number">178</span>, <span class="number">82</span>), <span class="number">1</span>, cc.color(<span class="number">239</span>, <span class="number">178</span>, <span class="number">82</span>));</span><br><span class="line"> <span class="keyword">this</span>.addChild(draw);</span><br><span class="line"> <span class="keyword">this</span>.addChild(draw2);</span><br><span class="line"> <span class="keyword">this</span>.schedule(<span class="keyword">this</span>.updateLoad, <span class="number">0.2</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> },</span><br><span class="line"> updateLoad: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> draw = <span class="keyword">this</span>.draw;</span><br><span class="line"> draw.clear();</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">this</span>.count ><span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.count = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>; i< <span class="keyword">this</span>.count; i++){</span><br><span class="line"> draw.drawRect(cc.p(<span class="number">40</span>*i, <span class="number">0</span>), cc.p(<span class="number">40</span>*i+<span class="number">20</span>, <span class="number">20</span>), cc.color(<span class="number">239</span>, <span class="number">178</span>, <span class="number">82</span>), <span class="number">1</span>, cc.color(<span class="number">239</span>, <span class="number">178</span>, <span class="number">82</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.count++;</span><br><span class="line"> },</span><br><span class="line"> onRemove: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"><span class="keyword">var</span> parent = <span class="keyword">this</span>.parent;</span><br><span class="line"> cc.eventManager.resumeTarget(parent,<span class="literal">true</span>);</span><br><span class="line">parent.resume();</span><br><span class="line">parent.removeChild(<span class="keyword">this</span>, <span class="literal">true</span>);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">LoaderLayer.preload = <span class="function"><span class="keyword">function</span>(<span class="params">url, data, cb, parent</span>)</span>{</span><br><span class="line"><span class="keyword">var</span> loader = cc.LoaderLayer;</span><br><span class="line"> <span class="keyword">if</span>(!loader) {</span><br><span class="line"> loader = <span class="keyword">new</span> LoaderLayer();</span><br><span class="line"> }</span><br><span class="line"><span class="comment">//parent.pause();</span></span><br><span class="line"> cc.eventManager.pauseTarget(parent,<span class="literal">true</span>);</span><br><span class="line"> parent.addChild(loader, <span class="number">99</span>);</span><br><span class="line"> Utils.ajax({</span><br><span class="line"> url: url,</span><br><span class="line"> type: <span class="string">"POST"</span>,</span><br><span class="line"> data: data,</span><br><span class="line"> success: <span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>{</span><br><span class="line"> loader.onRemove();</span><br><span class="line"> cb(data);</span><br><span class="line"> },</span><br><span class="line"> error: <span class="function"><span class="keyword">function</span>(<span class="params">xhr, status, msg</span>)</span>{</span><br><span class="line"> loader.onRemove();</span><br><span class="line"> alert(msg);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p></p><p>使用也很简单:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里默认使用了ajax的post方法,parent一般都是this。</span></span><br><span class="line">LoaderLayer.preload(url, data, cb, parent);</span><br></pre></td></tr></table></figure><p></p><p><strong>H5离线缓存</strong></p><p>HTML5离线存储Application Cache可以让用户在离线状态下浏览网站内容,缓存之后的内容不会再次从服务器获取,除非缓存文件改变了。如何使用呢?这需要打开你的html文件在里面:</p><p></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></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span> <span class="attr">manifest</span>=<span class="string">"manifest.appcache"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line">...</span><br></pre></td></tr></table></figure><p></p><p>接着在html文件的同级目录下面新建<code>manifest.appcache</code>,并加入如下内容:</p><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">// manifest.appcache</span><br><span class="line">CACHE MANIFEST</span><br><span class="line">#version 1.0</span><br><span class="line">CACHE:</span><br><span class="line">css/main.css</span><br><span class="line">img/test.png</span><br><span class="line"></span><br><span class="line">NETWORK:</span><br><span class="line">*</span><br></pre></td></tr></table></figure><p></p><p>关于manifest.appcache文件,基本格式为三段: CACHE, NETWORK,与 FALLBACK,其中NETWORK和FALLBACK为可选项,而第一行CACHE MANIFEST为固定格式,必须写在前面。</p><p>CACHE是必须参数,标识出哪些文件需要缓存,可以是相对路径也可以是绝对路径</p><p>NETWORK是可选参数,这一部分是要绕过缓存直接读取的文件,可以使用通配符*,也就是说除了上面的cache文件,剩下的文件每次都要重新拉取。</p><p>FALLBACK也是可选参数,指定了一个后备页面,当资源无法访问时,浏览器会使用该页面。该段落的每条记录都列出两个 URI—第一个表示资源,第二个表示后备页面。两个 URI 都必须使用相对路径并且与清单文件同源。可以使用通配符。</p><p>有了上面两个文件之后还要配置服务器的mime.types类型,找大盘apache的mime.types文件,添加</p><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">text/cache-manifest .appcache</span><br></pre></td></tr></table></figure><p></p><p>上面文件配置完成之后,application cache就可以运行了。</p><p>更新缓存的方式有三种:</p><ul><li>更新manifest文件</li></ul><p>可以修改一下manifest文件,把version改为1.1,然后刷新页面。</p><ul><li>通过javascript操作</li></ul><p>js添加一个监听事件:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.applicationCache.addEventListener(<span class="string">'updateready'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'updateready!'</span>);</span><br><span class="line"> <span class="built_in">window</span>.applicationCache.swapCache();</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><ul><li>清除浏览器缓存</li></ul><p>站点离线存储的容量限制是5M;如果manifest文件,或者内部列举的某一个文件不能正常下载,整个更新过程将视为失败,浏览器继续全部使用老的缓存。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2014/12/%E4%BD%BF%E7%94%A8cocos2d-js%E5%BC%80%E5%8F%91h5%E6%B8%B8%E6%88%8F/#disqus_thread</comments>
</item>
<item>
<title>Gulp上手</title>
<link>https://www.noonme.com/post/2014/10/getting-started-with-gulp/</link>
<guid>https://www.noonme.com/post/2014/10/getting-started-with-gulp/</guid>
<pubDate>Wed, 22 Oct 2014 03:10:56 GMT</pubDate>
<description>
<p>是时候抛弃繁重的Grunt了。Gulp是一个直观的、配置的、基于流的任务发布系统,而且它更高效。</p>
</description>
<content:encoded><![CDATA[<p>是时候抛弃繁重的Grunt了。Gulp是一个直观的、配置的、基于流的任务发布系统,而且它更高效。</p><a id="more"></a><p><img src="http://blog.u.qiniudn.com/uploads%2Fgulp-2x.png" alt="gulp"></p><p>为什么我会感兴趣呢?好问题。Gulp通过配置写代码不仅使得它编写任务简单,而且更加方便阅读和维护。</p><p>Gulp运用node.js的流,这使得它构建任务很快,因为没有磁盘文件的读写操作,如果你想了解更多关于流的知识,你可以看看<a href="https://github.com/substack/stream-handbook" target="_blank" rel="noopener">这个</a>。Gulp允许你输入源文件,然后在一系列的管道插件中处理,最后输出,不像Grunt你需要为每个插件配置输入和输出。下面就让我们通过一个sass编译的例子来看看Gulp和Grunt的差异吧。</p><p><strong>Grunt:</strong></p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">sass: {</span><br><span class="line"> dist: {</span><br><span class="line"> options: {</span><br><span class="line"> style: <span class="string">'expanded'</span></span><br><span class="line"> },</span><br><span class="line"> files: {</span><br><span class="line"> <span class="string">'dist/assets/css/main.css'</span>: <span class="string">'src/styles/main.scss'</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">autoprefixer: {</span><br><span class="line"> dist: {</span><br><span class="line"> options: {</span><br><span class="line"> browsers: [</span><br><span class="line"> <span class="string">'last 2 version'</span>, <span class="string">'safari 5'</span>, <span class="string">'ie 8'</span>, <span class="string">'ie 9'</span>, <span class="string">'opera 12.1'</span>, <span class="string">'ios 6'</span>, <span class="string">'android 4'</span></span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> src: <span class="string">'dist/assets/css/main.css'</span>,</span><br><span class="line"> dest: <span class="string">'dist/assets/css/main.css'</span></span><br><span class="line"> }</span><br><span class="line">},</span><br><span class="line"></span><br><span class="line">grunt.registerTask(<span class="string">'styles'</span>, [<span class="string">'sass'</span>, <span class="string">'autoprefixer'</span>]);</span><br></pre></td></tr></table></figure><p></p><p>Grunt要求每个插件配置要相互独立、要分别为每个插件配置输入源和输出路径。如,我们在sass插件里面配置了一个输入文件,然后保存输出。接着我们需要配置Autoprefixer的输入为Sass的输出,然后再输出了一个文件。让我们来看看Gulp是怎么做的:</p><p><strong>Gulp:</strong></p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'sass'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(<span class="string">'src/styles/main.scss'</span>)</span><br><span class="line"> .pipe(sass({ <span class="attr">style</span>: <span class="string">'compressed'</span> }))</span><br><span class="line"> .pipe(autoprefixer(<span class="string">'last 2 version'</span>, <span class="string">'safari 5'</span>, <span class="string">'ie 8'</span>, <span class="string">'ie 9'</span>, <span class="string">'opera 12.1'</span>, <span class="string">'ios 6'</span>, <span class="string">'android 4'</span>))</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/assets/css'</span>))</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>在Gulp中我们只配置一次输入文件,然后依次通过Sass插件处理,再传给<code>Autoprefixer</code>插件处理,然后我们得到输出文件。整个过程没有读取和写入不必要的文件,效率大大提高。</p><p>因此,你感兴趣了么?让我们从安装Gulp,创建基本的任务配置文件<code>gulpfile</code>开始吧。</p><p><strong>安装gulp</strong></p><p>在我们开始配置任务之前,我们先要安装gulp:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install gulp -g</span><br></pre></td></tr></table></figure><p></p><p>这样gulp就以全局的方式安装了,你可以在任何node命令行里面调用<code>gulp CLI</code>。然后我们需要在本地的某个项目里面使用<code>gulp</code>。使用<code>cd</code>命令进入到项目目录,运行下面的命令(先确保项目目录存在<code>package.json</code>文件):</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install gulp --save-dev</span><br></pre></td></tr></table></figure><p></p><p>这会把gulp安装到本地项目,并且把依赖的包写入到<code>package.json</code>文件的<code>devDependencies</code>里面</p><p><strong>安装gulp插件</strong></p><p>我们将会安装下列插件来开始我们的任务:</p><ul><li>Sass 编译 (<a href="https://github.com/sindresorhus/gulp-ruby-sass" target="_blank" rel="noopener">gulp-ruby-sass</a>)</li><li>添加浏览器前缀Autoprefixer(<a href="https://github.com/Metrime/gulp-autoprefixer" target="_blank" rel="noopener">gulp-autoprefixer</a>)</li><li>CSS压缩(<a href="https://github.com/jonathanepollack/gulp-minify-css" target="_blank" rel="noopener">gulp-minify-css</a>)</li><li>JS语法检查 (<a href="https://github.com/wearefractal/gulp-jshint" target="_blank" rel="noopener">gulp-jshint</a>)</li><li>文件合并 (<a href="https://github.com/wearefractal/gulp-concat" target="_blank" rel="noopener">gulp-concat</a>)</li><li>JS压Uglify (<a href="https://github.com/terinjokes/gulp-uglify" target="_blank" rel="noopener">gulp-uglify</a>)</li><li>图片压缩(<a href="https://github.com/sindresorhus/gulp-imagemin" target="_blank" rel="noopener">gulp-imagemin</a>)</li><li>LiveReload (<a href="https://github.com/vohof/gulp-livereload" target="_blank" rel="noopener">gulp-livereload</a>)</li><li>图片缓存,只压缩修改过的图片(<a href="https://github.com/jgable/gulp-cache/" target="_blank" rel="noopener">gulp-cache</a>)</li><li>修改提醒(<a href="https://github.com/mikaelbr/gulp-notify" target="_blank" rel="noopener">gulp-notify</a>)</li><li>文件清理 (<a href="https://www.npmjs.org/package/del" target="_blank" rel="noopener">del</a>)</li></ul><p>运行下面的命令安装这些插件:</p><p></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install gulp-ruby-sass gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-imagemin gulp-notify gulp-rename gulp-livereload gulp-cache del --save-dev</span><br></pre></td></tr></table></figure><p></p><p>这将会安装所有的依赖插件,并写入到package.json的devDependencies里面。所有的gulp插件列表可以<a href="http://gratimax.net/search-gulp-plugins/" target="_blank" rel="noopener">在这里</a>看到。</p><p><strong>加载插件</strong></p><p>我们需要创建一个<code>gulpfile.js</code>,然后使用这些插件:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">'gulp'</span>),</span><br><span class="line"> sass = <span class="built_in">require</span>(<span class="string">'gulp-ruby-sass'</span>),</span><br><span class="line"> autoprefixer = <span class="built_in">require</span>(<span class="string">'gulp-autoprefixer'</span>),</span><br><span class="line"> minifycss = <span class="built_in">require</span>(<span class="string">'gulp-minify-css'</span>),</span><br><span class="line"> jshint = <span class="built_in">require</span>(<span class="string">'gulp-jshint'</span>),</span><br><span class="line"> uglify = <span class="built_in">require</span>(<span class="string">'gulp-uglify'</span>),</span><br><span class="line"> imagemin = <span class="built_in">require</span>(<span class="string">'gulp-imagemin'</span>),</span><br><span class="line"> rename = <span class="built_in">require</span>(<span class="string">'gulp-rename'</span>),</span><br><span class="line"> concat = <span class="built_in">require</span>(<span class="string">'gulp-concat'</span>),</span><br><span class="line"> notify = <span class="built_in">require</span>(<span class="string">'gulp-notify'</span>),</span><br><span class="line"> cache = <span class="built_in">require</span>(<span class="string">'gulp-cache'</span>),</span><br><span class="line"> livereload = <span class="built_in">require</span>(<span class="string">'gulp-livereload'</span>),</span><br><span class="line"> del = <span class="built_in">require</span>(<span class="string">'del'</span>);</span><br></pre></td></tr></table></figure><p></p><p>我们也可以像grunt那样自动加载插件:<a href="https://github.com/jackfranklin/gulp-load-plugins" target="_blank" rel="noopener">auto load</a></p><p><strong>创建任务</strong></p><p><em>编译sass、加前缀、压缩</em></p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'styles'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(<span class="string">'src/styles/main.scss'</span>)</span><br><span class="line"> .pipe(sass({ <span class="attr">style</span>: <span class="string">'expanded'</span> }))</span><br><span class="line"> .pipe(autoprefixer(<span class="string">'last 2 version'</span>, <span class="string">'safari 5'</span>, <span class="string">'ie 8'</span>, <span class="string">'ie 9'</span>, <span class="string">'opera 12.1'</span>, <span class="string">'ios 6'</span>, <span class="string">'android 4'</span>))</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/assets/css'</span>))</span><br><span class="line"> .pipe(rename({<span class="attr">suffix</span>: <span class="string">'.min'</span>}))</span><br><span class="line"> .pipe(minifycss())</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/assets/css'</span>))</span><br><span class="line"> .pipe(notify({ <span class="attr">message</span>: <span class="string">'样式任务完成'</span> }));</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><blockquote><p>sass({ style: 'expanded' }:编译后保留原格式</p></blockquote><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'styles'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ ... )};</span><br></pre></td></tr></table></figure><p></p><p><code>gulp.task</code>API是用来创建任务的。然后通过命令<code>gulp styles</code>运行这个任务。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> gulp.src(<span class="string">'src/styles/main.scss'</span>)</span><br></pre></td></tr></table></figure><p></p><p><code>gulp.src</code>API用来配置输入的源文件。也可以用模式匹配,如<code>/**/*.scss</code>匹配所有文件夹下面后缀为<code>.scss</code>的文件作为输入。通过返回流使得它是异步的,确保在提醒任务完成的时候任务是完成了的。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.pipe(sass({ <span class="attr">style</span>: <span class="string">'expanded'</span> }))</span><br></pre></td></tr></table></figure><p></p><p>通过<code>.pipe()</code>把源文件流入一个插件的管道中。然后我们可以去插件的官网看看这个插件的详细用法。</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.pipe(gulp.dest(<span class="string">'dist/assets/css'</span>));</span><br></pre></td></tr></table></figure><p></p><p><code>gulp.dest</code>API是用来告知输出文件的路径的。一个任务可以有多个输出,如一个用来输出原来的版本(即源文件),一个输出处理后的版本(即输出文件)。你可以在上面的<code>styles</code>任务中看到。</p><p>建议去看<a href="https://github.com/gulpjs/gulp/blob/master/docs/API.md" target="_blank" rel="noopener">gulp api文档</a>,这样会更加清楚。</p><p><strong>js语法检查、合并和压缩任务</strong></p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'scripts'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(<span class="string">'src/scripts/**/*.js'</span>)</span><br><span class="line"> .pipe(jshint(<span class="string">'.jshintrc'</span>))</span><br><span class="line"> .pipe(jshint.reporter(<span class="string">'default'</span>))</span><br><span class="line"> .pipe(concat(<span class="string">'main.js'</span>))</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/assets/js'</span>))</span><br><span class="line"> .pipe(rename({<span class="attr">suffix</span>: <span class="string">'.min'</span>}))</span><br><span class="line"> .pipe(uglify())</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/assets/js'</span>))</span><br><span class="line"> .pipe(notify({ <span class="attr">message</span>: <span class="string">'Scripts task complete'</span> }));</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>这里用的<code>JSHin</code>t插件,我们使用了默认的<code>JSHint Reporter</code>,可能适用于大多数人,想了解更多可以去<a href="http://www.jshint.com/docs/reporters/" target="_blank" rel="noopener">jshint官网</a>看</p><p><strong>图片压缩任务</strong></p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'images'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(<span class="string">'src/images/**/*'</span>)</span><br><span class="line"> .pipe(imagemin({ <span class="attr">optimizationLevel</span>: <span class="number">3</span>, <span class="attr">progressive</span>: <span class="literal">true</span>, <span class="attr">interlaced</span>: <span class="literal">true</span> }))</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/assets/img'</span>))</span><br><span class="line"> .pipe(notify({ <span class="attr">message</span>: <span class="string">'Images task complete'</span> }));</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>这里我们只用了<code>imagemin</code>插件,但是可以做的更好,我们可以缓存修改过的图片,或者只对修改过的图片进行再次的压缩操作,因此我们可以使用<a href="https://github.com/jgable/gulp-cache" target="_blank" rel="noopener">gulp-cahce</a>插件,因此我们需要将这行代码:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.pipe(imagemin({ <span class="attr">optimizationLevel</span>: <span class="number">3</span>, <span class="attr">progressive</span>: <span class="literal">true</span>, <span class="attr">interlaced</span>: <span class="literal">true</span> }))</span><br></pre></td></tr></table></figure><p></p><p>改成:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">.pipe(cache(imagemin({ <span class="attr">optimizationLevel</span>: <span class="number">5</span>, <span class="attr">progressive</span>: <span class="literal">true</span>, <span class="attr">interlaced</span>: <span class="literal">true</span> })))</span><br></pre></td></tr></table></figure><p></p><p>此时,只有新的图片或者改变过的图片才会被压缩。</p><p><strong>文件清理</strong></p><p>在再次发布之前,我们最好把目标文件的文件先清理掉,然后重新构建:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'clean'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>{</span><br><span class="line"> del([<span class="string">'dist/assets/css'</span>, <span class="string">'dist/assets/js'</span>, <span class="string">'dist/assets/img'</span>], cb)</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p><strong>默认任务</strong></p><p>我们可以通过<code>$ gulp</code>启动默认任务,然后在默认任务中调用其他任务:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'default'</span>, [<span class="string">'clean'</span>], <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line">gulp.start(<span class="string">'styles'</span>, <span class="string">'scripts'</span>, <span class="string">'images'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>看到<code>gulp.task</code>里面的数组了吧?这里定义了任务的依赖,也就是说<code>default</code>任务依赖<code>clean</code>任务。在这个例子中,执行<code>gulp.start</code>之前会先运行<code>clean</code>任务。Gulp里面的任务同时进行,没有明确的顺序哪个先完成,所以我们要确保<code>clean</code>任务执行完之后再执行<code>gulp.start</code>里面的任务。</p><blockquote><p>虽然不建议在执行依赖任务数组的时候使用<code>gulp.start</code>,但是在这里我们没有办法确保<code>clean</code>任务执行完毕后再执行其它任务,因此这里使用<code>gulp.start</code>貌似是最好的选择。</p></blockquote><p><strong>Watch任务</strong></p><p>当文件发生变化的时候,我们可能需要重新执行任务,因此我们需要配置一个监听文件变化的任务:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'watch'</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 class="comment">// Watch .scss files</span></span><br><span class="line"> gulp.watch(<span class="string">'src/styles/**/*.scss'</span>, [<span class="string">'styles'</span>]);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Watch .js files</span></span><br><span class="line"> gulp.watch(<span class="string">'src/scripts/**/*.js'</span>, [<span class="string">'scripts'</span>]);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Watch image files</span></span><br><span class="line"> gulp.watch(<span class="string">'src/images/**/*'</span>, [<span class="string">'images'</span>]);</span><br><span class="line"></span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>我们通过<code>gulp.watch</code>API来监听文件的变化,然后执行相关的依赖任务。现在我们可以执行<code>$ gulp watch</code>命令来执行我们的<code>watch</code>任务,监听<code>.scss</code>、<code>.js</code>或者图片文件的变化执行相应的任务。</p><p><strong>LiveReload任务</strong></p><p>当我们代码修改的时候,Gulp也可以主动帮我们刷新页面,此时我们需要配置<code>LiveReload</code>服务,并修改我们的<code>watch</code>任务:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">gulp.task(<span class="string">'watch'</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 class="comment">// Create LiveReload server</span></span><br><span class="line"> livereload.listen();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Watch any files in dist/, reload on change</span></span><br><span class="line"> gulp.watch([<span class="string">'dist/**'</span>]).on(<span class="string">'change'</span>, livereload.changed);</span><br><span class="line"></span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>要让这个任务生效,我们还需要安装并开启浏览器LiveReload插件,我们也可以<a href="http://feedback.livereload.com/knowledgebase/articles/86180-how-do-i-add-the-script-tag-manually-" target="_blank" rel="noopener">手动添加代码片段</a>。</p><p><strong>整合这些任务</strong></p><p>把上面的这些任务综合起来,就构成了一个完整的<code>gulpfile</code>:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// gulpfile.js</span></span><br><span class="line"><span class="comment">// Load plugins</span></span><br><span class="line"><span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">'gulp'</span>),</span><br><span class="line"> sass = <span class="built_in">require</span>(<span class="string">'gulp-ruby-sass'</span>),</span><br><span class="line"> autoprefixer = <span class="built_in">require</span>(<span class="string">'gulp-autoprefixer'</span>),</span><br><span class="line"> minifycss = <span class="built_in">require</span>(<span class="string">'gulp-minify-css'</span>),</span><br><span class="line"> jshint = <span class="built_in">require</span>(<span class="string">'gulp-jshint'</span>),</span><br><span class="line"> uglify = <span class="built_in">require</span>(<span class="string">'gulp-uglify'</span>),</span><br><span class="line"> imagemin = <span class="built_in">require</span>(<span class="string">'gulp-imagemin'</span>),</span><br><span class="line"> rename = <span class="built_in">require</span>(<span class="string">'gulp-rename'</span>),</span><br><span class="line"> concat = <span class="built_in">require</span>(<span class="string">'gulp-concat'</span>),</span><br><span class="line"> notify = <span class="built_in">require</span>(<span class="string">'gulp-notify'</span>),</span><br><span class="line"> cache = <span class="built_in">require</span>(<span class="string">'gulp-cache'</span>),</span><br><span class="line"> livereload = <span class="built_in">require</span>(<span class="string">'gulp-livereload'</span>),</span><br><span class="line"> del = <span class="built_in">require</span>(<span class="string">'del'</span>);</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Styles</span></span><br><span class="line">gulp.task(<span class="string">'styles'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(<span class="string">'src/styles/main.scss'</span>)</span><br><span class="line"> .pipe(sass({ <span class="attr">style</span>: <span class="string">'expanded'</span>, }))</span><br><span class="line"> .pipe(autoprefixer(<span class="string">'last 2 version'</span>, <span class="string">'safari 5'</span>, <span class="string">'ie 8'</span>, <span class="string">'ie 9'</span>, <span class="string">'opera 12.1'</span>, <span class="string">'ios 6'</span>, <span class="string">'android 4'</span>))</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/styles'</span>))</span><br><span class="line"> .pipe(rename({ <span class="attr">suffix</span>: <span class="string">'.min'</span> }))</span><br><span class="line"> .pipe(minifycss())</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/styles'</span>))</span><br><span class="line"> .pipe(notify({ <span class="attr">message</span>: <span class="string">'Styles task complete'</span> }));</span><br><span class="line">});</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Scripts</span></span><br><span class="line">gulp.task(<span class="string">'scripts'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(<span class="string">'src/scripts/**/*.js'</span>)</span><br><span class="line"> .pipe(jshint(<span class="string">'.jshintrc'</span>))</span><br><span class="line"> .pipe(jshint.reporter(<span class="string">'default'</span>))</span><br><span class="line"> .pipe(concat(<span class="string">'main.js'</span>))</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/scripts'</span>))</span><br><span class="line"> .pipe(rename({ <span class="attr">suffix</span>: <span class="string">'.min'</span> }))</span><br><span class="line"> .pipe(uglify())</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/scripts'</span>))</span><br><span class="line"> .pipe(notify({ <span class="attr">message</span>: <span class="string">'Scripts task complete'</span> }));</span><br><span class="line">});</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Images</span></span><br><span class="line">gulp.task(<span class="string">'images'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> gulp.src(<span class="string">'src/images/**/*'</span>)</span><br><span class="line"> .pipe(cache(imagemin({ <span class="attr">optimizationLevel</span>: <span class="number">3</span>, <span class="attr">progressive</span>: <span class="literal">true</span>, <span class="attr">interlaced</span>: <span class="literal">true</span> })))</span><br><span class="line"> .pipe(gulp.dest(<span class="string">'dist/images'</span>))</span><br><span class="line"> .pipe(notify({ <span class="attr">message</span>: <span class="string">'Images task complete'</span> }));</span><br><span class="line">});</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Clean</span></span><br><span class="line">gulp.task(<span class="string">'clean'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">cb</span>) </span>{</span><br><span class="line"> del([<span class="string">'dist/assets/css'</span>, <span class="string">'dist/assets/js'</span>, <span class="string">'dist/assets/img'</span>], cb)</span><br><span class="line">});</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Default task</span></span><br><span class="line">gulp.task(<span class="string">'default'</span>, [<span class="string">'clean'</span>], <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> gulp.start(<span class="string">'styles'</span>, <span class="string">'scripts'</span>, <span class="string">'images'</span>);</span><br><span class="line">});</span><br><span class="line"> </span><br><span class="line"><span class="comment">// Watch</span></span><br><span class="line">gulp.task(<span class="string">'watch'</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 class="comment">// Watch .scss files</span></span><br><span class="line"> gulp.watch(<span class="string">'src/styles/**/*.scss'</span>, [<span class="string">'styles'</span>]);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Watch .js files</span></span><br><span class="line"> gulp.watch(<span class="string">'src/scripts/**/*.js'</span>, [<span class="string">'scripts'</span>]);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Watch image files</span></span><br><span class="line"> gulp.watch(<span class="string">'src/images/**/*'</span>, [<span class="string">'images'</span>]);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Create LiveReload server</span></span><br><span class="line"> livereload.listen();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Watch any files in dist/, reload on change</span></span><br><span class="line"> gulp.watch([<span class="string">'dist/**'</span>]).on(<span class="string">'change'</span>, livereload.changed);</span><br><span class="line"> </span><br><span class="line">});</span><br></pre></td></tr></table></figure><p></p><p>如有任何问题,可以在下面评论。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2014/10/getting-started-with-gulp/#disqus_thread</comments>
</item>
<item>
<title>MarkdownPad注册码</title>
<link>https://www.noonme.com/post/2014/10/MarkdownPad%E6%B3%A8%E5%86%8C%E7%A0%81/</link>
<guid>https://www.noonme.com/post/2014/10/MarkdownPad%E6%B3%A8%E5%86%8C%E7%A0%81/</guid>
<pubDate>Wed, 22 Oct 2014 03:01:12 GMT</pubDate>
<description>
<p>MarkdownPad 是 Windows 平台上一个功能完善的 Markdown 编辑器。应该说是最好用的了。但是免费版有一些限制,还好终于找到了可用的专业版激活码,共享给大家。</p>
</description>
<content:encoded><![CDATA[<p>MarkdownPad 是 Windows 平台上一个功能完善的 Markdown 编辑器。应该说是最好用的了。但是免费版有一些限制,还好终于找到了可用的专业版激活码,共享给大家。</p><a id="more"></a><p>截图为证:</p><p><img src="http://blog.u.qiniudn.com/uploads%2Fmarkdownpad.png" alt></p><p><strong>专为 Markdown 打造</strong></p><p>提供了语法高亮和方便的快捷键功能,给您最好的 Markdown 编写体验。</p><p>来试一下:</p><ul><li>粗体 (Ctrl+B) and 斜体 (Ctrl+I)</li><li>引用 (Ctrl+Q)</li><li>代码块 (Ctrl+K)</li><li>标题 1, 2, 3 (Ctrl+1, Ctrl+2, Ctrl+3)</li><li>列表 (Ctrl+U and Ctrl+Shift+O)</li></ul><p><strong>实时预览,所见即所得</strong></p><p>无需猜测您的<a href="http://markdownpad.com/" target="_blank" rel="noopener">语法</a>是否正确;每当您敲击键盘,实时预览功能都会立刻准确呈现出文档的显示效果。</p><p><strong>为高级用户而设计的稳定的 Markdown 编辑器</strong></p><p>MarkdownPad 支持多种 Markdown 解析引擎,包括 标准 Markdown 、 Markdown 扩展 (包括表格支持) 以及 GitHub 风格 Markdown 。</p><p>有了标签式多文档界面、PDF 导出、内置的图片上传工具、会话管理、拼写检查、自动保存、语法高亮以及内置的 CSS 管理器,您可以随心所欲地使用 MarkdownPad。</p><p><strong>激活信息</strong></p><p>邮箱地址:</p><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Soar360@live.com</span><br></pre></td></tr></table></figure><p></p><p>授权秘钥:</p><p></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GBPduHjWfJU1mZqcPM3BikjYKF6xKhlKIys3i1MU2eJHqWGImDHzWdD6xhMNLGVpbP2M5SN6bnxn2kSE8qHqNY5QaaRxmO3YSMHxlv2EYpjdwLcPwfeTG7kUdnhKE0vVy4RidP6Y2wZ0q74f47fzsZo45JE2hfQBFi2O9Jldjp1mW8HUpTtLA2a5/sQytXJUQl/QKO0jUQY4pa5CCx20sV1ClOTZtAGngSOJtIOFXK599sBr5aIEFyH0K7H4BoNMiiDMnxt1rD8Vb/ikJdhGMMQr0R4B+L3nWU97eaVPTRKfWGDE8/eAgKzpGwrQQoDh+nzX1xoVQ8NAuH+s4UcSeQ==</span><br></pre></td></tr></table></figure><p></p><p><strong>其他</strong></p><p>不知道为何,MarkdownPad 2 的注册私钥泄密了,按照这个逻辑,是可以做成一个注册机的。附送私钥如下。</p><p></p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">-----BEGIN RSA PRIVATE KEY-----</span><br><span class="line">Proc-Type: 4,ENCRYPTED</span><br><span class="line">DEK-Info: DES-EDE3-CBC,CCE015B0820B9A3F</span><br><span class="line"></span><br><span class="line">4BiGKVEzt+cLBdilEZR+8Y0YHrarjNX4bT5XbQ5BgTcJwMYnARCKib+BgwX9wPUB</span><br><span class="line">3D7PUwt4m4OGemXXcC4ELUFyMqHIT6SILFSdTTIvnQbsWutx44zyJK9SlP/2uhG8</span><br><span class="line">JIUDdLWVv5kS8J/UcInC5bomKZ5SX3+0ziRpLWkgXoHJ5gjQ+fDNzAKLcNn7Bubq</span><br><span class="line">znjoSH8XuGnORp7d4FiRT5Vl9WpnBqyyFSkYwzDOK//iFgHyhYZ5BCInfQMX66Cj</span><br><span class="line">wk3Pax/ujSDzYalkkJe8o8VAcEisMoYhSTS9BnRpROn6V4UyAugWMPnEOgiBmO42</span><br><span class="line">HwLoqK7WXCe+wS1UiDnECU2wIU5Z8qL5oKEKFGukakK+mYXh8MHTkYq2ZQZBo8Vy</span><br><span class="line">venhBaolrViCWdkJ5MsuqarBJjyCeAS38GAJURxsKnd+Bp7gGAlNI/0OWIcrjIi4</span><br><span class="line">4o/0CaYRHR+PdeuJup5rUKPgGfMW0iOwmMncSM19hIiaU4/hHGYeXrL1aKfZX4ML</span><br><span class="line">YxWJ/i6u8U3gcJo/bOvjirxUZOLTfv3n5aDTcxU70ejgmmYLBaxKt32Ib9oLwnL8</span><br><span class="line">AUnWNRPZaNJ/X853ybsGu73vA/6HPw63g0EaNharg21Q1IhuggMvHJiN6Z9d4KGg</span><br><span class="line">Vio6++15SVcsyQEGZx+jjv95IEfgwUQXBmkNRZ/KJQQLkKuOuXuvakI5sWdNJLRo</span><br><span class="line">xY9W2lHMnY/Yi82V4P5odxFZOmAYK5DHCQ47gw9IzqGjCyxATAYgkoZR+PCP5y9n</span><br><span class="line">IZ8oRYbNpykXVvQC0JALgixIZaAjj30wXb1CcD3layQNkrp0O4qVzuqyT7e2aKEq</span><br><span class="line">XNeoB0uDNvn10L60QAOG5630uIIFRyXphZgrzScp97ABq7STQ7t4zTZhbmiwnPHq</span><br><span class="line">8xft+mO2rW3puYEHoEkQKwJcPUTpR/s67IIXQMs1y93WpJoOw7doumyNtAoR8EfK</span><br><span class="line">m/H4UoJhoiXL755n4CGnET+LEtuFO/0UwdWxj+u503ovWL021DAnKhrGPMpo73ga</span><br><span class="line">aL/OBSFSFyxY92qGqfMWNQOaoK5hCRhzp/8Pd+B1tjhnxUNOl1ulytmhJBigU5nV</span><br><span class="line">arkgJYmG+Ox15f1YWxmD1EhLOQLUgBruG04cjRnG4LXV+UBKRdcS3bECArFutlTw</span><br><span class="line">6ogbi8cbDJNBbGpnlfIbEgGEe423rCTuoLJcIZfIDvPDsMobd9lCgn2o95N3Ey4U</span><br><span class="line">+UT1PEDEhXW0nggdvZGy8y4T0ZIJ6Tn4asWHTsqH1fQYEz3H51pMehfCbxbc6Bbh</span><br><span class="line">nuNyrjv0NSNJvOAn71tFPRf6vgO83h5EFS0ov5ASbWchuf7BFpebmOO04aVMXPBe</span><br><span class="line">3nfE1L5in5ApK7AyhfdSPpELCxBUQ0av4k/0yw/P2TRllybgjdoXjlsJqBn7rkqr</span><br><span class="line">4u1H7pytlEWt0mSgZenlGXrOOEaknUe40z9BHNxliA7ET1MQk6veFVfbSFjeVUDk</span><br><span class="line">fsFAVE88/KHyMMfGwwcuLdnQnOBHNJ8iJjJF+RZy93QzveZB22+m5cl9SNXIMcEA</span><br><span class="line">mOm4Q8CaLwwzZmF9NKV2CjlmUTqOszJv5abS5YdrPavAR6QxGjZfya3nA2ASd6HN</span><br><span class="line">-----END RSA PRIVATE KEY-----</span><br></pre></td></tr></table></figure><p></p>]]></content:encoded>
<comments>https://www.noonme.com/post/2014/10/MarkdownPad%E6%B3%A8%E5%86%8C%E7%A0%81/#disqus_thread</comments>
</item>
<item>
<title>js表格导出文件</title>
<link>https://www.noonme.com/post/2014/10/js%E8%A1%A8%E6%A0%BC%E5%AF%BC%E5%87%BA%E6%96%87%E4%BB%B6/</link>
<guid>https://www.noonme.com/post/2014/10/js%E8%A1%A8%E6%A0%BC%E5%AF%BC%E5%87%BA%E6%96%87%E4%BB%B6/</guid>
<pubDate>Fri, 17 Oct 2014 06:16:15 GMT</pubDate>
<description>
<p>最近项目中做了一个<code>table</code>导出文件的东西,网上找了好久但是很多缺陷,要么就是很大一个库,要么就是中文乱码,于是自己弄了一下,很小,而且不会中文乱码,现在共享出来。</p>
</description>
<content:encoded><![CDATA[<p>最近项目中做了一个<code>table</code>导出文件的东西,网上找了好久但是很多缺陷,要么就是很大一个库,要么就是中文乱码,于是自己弄了一下,很小,而且不会中文乱码,现在共享出来。</p><a id="more"></a><p><strong><a href="http://w3cboy.com/demo/tableExport/" target="_blank" rel="noopener">demo</a></strong></p><p><strong><a href="https://github.com/huanz/tableExport" target="_blank" rel="noopener">github代码</a></strong></p><p>还很不完善,没有作太多的兼容处理,目前只做了<code>json</code>、<code>txt</code>、<code>csv</code>、<code>xsl</code>、<code>doc</code>,要加<code>pdf</code>或者图片什么的可能要加入更多的依赖库,所以我没有做,需要的话自己加进去。</p><p>原始的代码是可以不依赖别的任何库的(jquery只是因为项目中用了所以直接写成了jquery插件的形式):</p><p><img src="http://blog.u.qiniudn.com/uploads%2FtableExport.jpg" alt></p><p>原始的代码在<code>jquery.tableExport.js</code>中,用法如下:</p><p></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">a</span> <span class="attr">download</span>=<span class="string">"filename.json"</span> <span class="attr">href</span>=<span class="string">"javascript:;"</span> <span class="attr">onclick</span>=<span class="string">"$('#J-table').tableExport(this, 'json');"</span>></span>JSON<span class="tag"></<span class="name">a</span>></span></span><br></pre></td></tr></table></figure><p></p><p>通过浏览器的<code>download</code>属性来实现文件名设置,这个目前只有firefox和chrome支持这个属性。</p><h3 id="tableexport-js">tableExport.js</h3><p>后来稍微作了一下修改,加入了两个依赖库:<a href="https://github.com/eligrey/FileSaver.js" target="_blank" rel="noopener">FileSaver.js</a> 和 <a href="https://github.com/eligrey/Blob.js" target="_blank" rel="noopener">Blob.js</a></p><p>在导出的时候只提取<code>table</code>里面的文本信息,链接什么的会被过滤掉,代码也很简单,使用也很简单:</p><p></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">document</span>.getElementById(<span class="string">'xls'</span>).addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>{</span><br><span class="line"> e.preventDefault();</span><br><span class="line"><span class="comment">// tableId filename filetype:json txt csv doc xls</span></span><br><span class="line">tableExport(<span class="string">'table1'</span>, <span class="string">'测试测试'</span>, <span class="string">'doc'</span>);</span><br><span class="line">}, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure><p></p><p>想用的可以下载下来根据自己的需要修改。</p>]]></content:encoded>
<comments>https://www.noonme.com/post/2014/10/js%E8%A1%A8%E6%A0%BC%E5%AF%BC%E5%87%BA%E6%96%87%E4%BB%B6/#disqus_thread</comments>
</item>
<item>
<title>Github免费送DigitalOcean 100刀和免费me域名一年</title>
<link>https://www.noonme.com/post/2014/10/github-digitalocean/</link>
<guid>https://www.noonme.com/post/2014/10/github-digitalocean/</guid>
<pubDate>Mon, 13 Oct 2014 11:07:09 GMT</pubDate>
<description>
<p>最近github有一个针对学生的活动,免费赠送100刀到DO的账户,另外还免费赠送一个namecheap的域名和一个SSL免费使用一年。</p>
</description>
<content:encoded><![CDATA[<p>最近github有一个针对学生的活动,免费赠送100刀到DO的账户,另外还免费赠送一个namecheap的域名和一个SSL免费使用一年。</p><a id="more"></a><p>DigitalOcean,简称DO,是美国一家很有实力的云主机提供商,已经获得多轮投资,最近一轮是3700万美元,所以财大气粗,经常撒钱。</p><p>Do现在提供<code>阿姆斯特丹</code>、<code>伦敦</code>、<code>新加坡</code>、<code>纽约</code>、<code>圣弗朗西斯科(旧金山)</code>五个数据中心,其中以<code>旧金山</code>对国内访问最好,其次纽约,新加坡仅仅是ping值较低,但他的线路是绕道美国西海岸上岸然后再绕回中国,所以线路并不好。</p><p>Do最低配置是512M内存,20G SSD硬盘 ,1T月流量,配置还是很给力的,加上DO的技术,很值得入手。</p><p>##申请可以看下面的教程</p><p>###1.首先你要有一个paypal账户</p><p>因为DO需要最低充值5刀才能激活账户,激活账户后才能使用优惠码,可选信用卡和paypal充值,这里当然选paypal最好。</p><p>地址:<a href="http://www.paypal.com" target="_blank">http://www.paypal.com</a></p><p><strong>去paypal去注册个账户</strong></p><p>注册过程中会让你绑定一张信用卡,其实有网银的银联的借记卡都可以绑定,支付的时候会跳转到相应银行的网上支付页面,并且兑换成人民币结算。所以不用担心没有信用卡和美元。</p><p>###2.咱们还需要一个edu邮箱</p><p>网上有很多获取教育邮箱的教程,但好像都不能用,即使能申请,可能也不能用于获取优惠码</p><p><a href="https://edu.yao.lv/?t12l_language_selector=cn&r=http%3A%2F%2Fedu.yao.lv%2F" target="_blank">https://edu.yao.lv/</a></p><p>这个地址可以申请一个临时的edu邮箱,可以使用15分钟,时间不够还可以续15分钟</p><p><img src="http://lomu.me/uploads/2014/1013144701.jpg" alt="edu邮箱"></p><p>点随机生成临时邮箱,这样就会申请到一个临时的edu邮箱,不要关闭页面,一会还要用到。</p><p>###3.申请一个github账号</p><p>打开<a href="http://github.com" target="_blank">http://github.com</a></p><p><img src="http://lomu.me/uploads/2014/1013144702.jpg" alt="github账号"></p><p>页面的右侧 第一个是用户名,第二个是email,第三个是密码,email就用你刚才申请到的那个临时的邮箱就好了</p><p>接着点sign up for github, 跳到第二步,直接点filsh sign up就可以了,这样就注册成功!</p><p>然后打开刚才你注册邮箱的那个页面,查看邮件,如果没有,就先刷新一下页面。</p><p>会看到两封邮件,最下面一封就是验证邮件。</p><p>点左边的<code>显示</code>,里面有个网址,就是git的验证地址,复制到浏览器,打开,点右边的<code>confirm</code>就激活了账号</p><p>###4.申请优惠码</p><p>打开:<a href="https://education.github.com/pack" target="_blank">https://education.github.com/pack</a></p><p>点get your pack,进入下一个页面,yes , i’m a student. 需要填写的 只有一个how do you plan to use github?</p><p>随便写一个,比如T‘ll public my program at github.</p><p>然后点submit request,然后出现一个页面</p><p>Thanks for submitting! You should be getting an email from us within a week. Have an Octotastic day!</p><p>当然不是要1个星期才能使用,重新打开<a href="https://education.github.com/pack" target="_blank">https://education.github.com/pack</a></p><p>然后点get your pack</p><p>然后按钮就消失,然后你就可以使用所有github提供的免费的东西了</p><p>下拉,其中两个namecheap图标的一个就是免费一年的me域名,一个是免费一年的SSL证书,点蓝色的 you unique link就可以去注册一个me域名,先搜索你要注册域名有没有注册,然后逐步操作,创建一个nc的账号。 SSL先点you offer code ,然后会显示优惠码,点namecheap website 就进入ssl购买链接,登陆你NC账号,购买SSL过程中在右侧 prom code中输入这个优惠码就变成0了,这两个不多说</p><p>上面那个DigitalOcean图标就是我们需要的了,点your offer code 就会生成一个优惠码,如图:</p><p><img src="http://lomu.me/uploads/2014/1013144703.jpg" alt="优惠码"></p><p>记住这个优惠码,然后让我们看下一步</p><p>5.注册digitalocean并使用优惠码</p><p>打开<a href="https://www.digitalocean.com/?refcode=dc27ee8be3e6" target="_blank">https://www.digitalocean.com/?refcode=dc27ee8be3e6</a></p><p>最好使用我的推荐链接,这样会多10刀,直接打开没有这10美元哦~</p><p><img src="http://lomu.me/uploads/2014/1013144704.jpg" alt="DigitalOcean"></p><p>直接填写email地址和密码,然后点create account,就创建一个账号了,email请不要再用那个临时邮箱了,用你自己的邮箱。</p><p>进入你的邮箱,打开DO发来的验证邮件 ,点CONFIRM EMAIL验证邮箱</p><p>验证会进入一个界面,下拉到最下面</p><p><img src="http://lomu.me/uploads/2014/1013144705.jpg" alt="DigitalOcean"></p><p>如果你显示的不是这个页面,点左侧的billing就能看到了</p><p>然后在Promo Code中输入你在上一步得到的优惠码,如果没反应的话,你可以先删除最后一个字符,然后再输入这个字符就行了</p><p>然后会显示You have received $100.00 in credits!</p><p>这时候你账户里有110刀了,但是还不能使用的哦,还有一步激活账户。</p><p>###6.激活账户</p><p>点DO左侧的billing,然后右边</p><p>PayPal Payment <img src="http://lomu.me/uploads/2014/1013144706.jpg" alt="DigitalOcean"></p><p>点paynow,就会进入palpay支付,会自动切换到中文,有两种方式付款,一种是用你的paypal账号付款,一种是直接用银行卡或借计卡付款</p><p>,借计卡支付的是人民币,信用卡支付的是美元,5美元应该是30来块钱,不过建行的汇率可能会高一些不划算。 paypal要比支付宝安全很多,很容易争议退款。 支付完后账户就能使用了哦,会有115刀哦,用5刀的配置可以用23个月</p><p>###7.创建VPS</p><p>点左侧的DropLet,然后点右边的create droplet</p><p><strong>1.Droplet Hostname</strong></p><p>随便给你的计划起个名字</p><p><strong>2.Select Size</strong></p><p>配置</p><p>DO技术很好 又是SSD ,一般第一个配置512M的就够用,5刀能用23个月,千万注意默认不是这个哦,别选错了哦</p><p><strong>3.Select Region</strong></p><p>前面就说了,选第三个SAN FRANCISCO,也就是<code>旧金山</code></p><p><strong>4.Select Image</strong></p><p>安装系统</p><p>建议选择Centos6.5 x32</p><p>最后点create droplet</p><p>等个40秒就创建成功,然后点power cicle就启动了VPS</p><p>打开你的注册邮箱,有封邮件里面就是你的vps的root密码</p><p>用putty登陆VPS后要修改密码,按提示操作,是先输入一次旧密码,然后再输入两次新密码。</p><p>##提醒大家几点:</p><ol><li>不要刷,有一个够用就可以了,刷的可能会退款封号的;</li><li>一个palpay只激活一个账号,多了可能封IP;</li><li>不要贪得无厌,一口气弄好几个,最后鸡飞蛋打。</li></ol>]]></content:encoded>
<comments>https://www.noonme.com/post/2014/10/github-digitalocean/#disqus_thread</comments>
</item>
</channel>
</rss>