-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
1345 lines (645 loc) · 90.1 KB
/
Copy pathindex.html
File metadata and controls
1345 lines (645 loc) · 90.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html>
<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
<link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
<link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />
<meta name="keywords" content="Hexo, NexT" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0" />
<meta name="description" content="私、気になります!">
<meta property="og:type" content="website">
<meta property="og:title" content="前端学习之路">
<meta property="og:url" content="http://glassysky.github.io/index.html">
<meta property="og:site_name" content="前端学习之路">
<meta property="og:description" content="私、気になります!">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="前端学习之路">
<meta name="twitter:description" content="私、気になります!">
<script type="text/javascript" id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Pisces',
sidebar: {"position":"left","display":"post","offset":12,"offset_float":0,"b2t":false,"scrollpercent":false},
fancybox: true,
motion: true,
duoshuo: {
userId: '0',
author: '博主'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
</script>
<link rel="canonical" href="http://glassysky.github.io/"/>
<title> 前端学习之路 </title>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<script type="text/javascript">
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?19e25f24e7a22e21d7d506b65abb0cda";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<div class="container one-collumn sidebar-position-left
page-home
">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-wrapper">
<div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">前端学习之路</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<p class="site-subtitle"></p>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section">
<i class="menu-item-icon fa fa-fw fa-home"></i> <br />
首页
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives" rel="section">
<i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
归档
</a>
</li>
<li class="menu-item menu-item-commonweal">
<a href="/404.html" rel="section">
<i class="menu-item-icon fa fa-fw fa-heartbeat"></i> <br />
公益404
</a>
</li>
</ul>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<section id="posts" class="posts-expand">
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://glassysky.github.io/2017/03/18/web-app-summary/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tianhao Cui">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="前端学习之路">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/03/18/web-app-summary/" itemprop="url">
WebApp 开发总结
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-03-18T12:52:32+08:00">
2017-03-18
</time>
</span>
<span class="post-comments-count">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-comment-o"></i>
</span>
<a href="/2017/03/18/web-app-summary/#comments" itemprop="discussionUrl">
<span class="post-comments-count disqus-comment-count" data-disqus-identifier="2017/03/18/web-app-summary/" itemprop="commentCount"></span>
</a>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://glassysky.github.io/2017/03/16/what-happens-when-you-type-in-a-url-in-browser/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tianhao Cui">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="前端学习之路">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/03/16/what-happens-when-you-type-in-a-url-in-browser/" itemprop="url">
在浏览器地址栏键入url之后发生了什么
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-03-16T14:47:48+08:00">
2017-03-16
</time>
</span>
<span class="post-comments-count">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-comment-o"></i>
</span>
<a href="/2017/03/16/what-happens-when-you-type-in-a-url-in-browser/#comments" itemprop="discussionUrl">
<span class="post-comments-count disqus-comment-count" data-disqus-identifier="2017/03/16/what-happens-when-you-type-in-a-url-in-browser/" itemprop="commentCount"></span>
</a>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>本篇均以输入’www.baidu.com’为例</p>
<h1 id="DNS解析"><a href="#DNS解析" class="headerlink" title="DNS解析"></a>DNS解析</h1><p>网络通讯大多数都是基于<code>TCP/IP</code>的,而<code>TCP/IP</code>又是基于<code>IP</code>的。我们输入的域名并不能被计算机网络所识别,因此需要将域名转为IP,而这个过程叫就做DNS解析。</p>
<h2 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h2><h3 id="DNS-Domain-Name-System"><a href="#DNS-Domain-Name-System" class="headerlink" title="DNS(Domain Name System)"></a>DNS(Domain Name System)</h3><p>即<strong>计算机域名系统</strong>,它由域名解析器和域名服务器组成。</p>
<p>域名服务器:指保存有该网络中所有主机的域名和对应IP地址,并具有将域名转换为IP地址功能的服务器。<br>其中域名必须对应一个,且只能对应一个IP地址;而IP地址不一定有域名而且可以对应多个域名。</p>
<p>DNS也是应用层协议,但他是为了其他应用层协议工作的,用于将用户提供的域名解析为IP地址。</p>
<p>DNS主要基于UDP传输层协议,</p>
<h3 id="根域"><a href="#根域" class="headerlink" title="根域"></a>根域</h3><p>根域就是所谓的<code>.</code>。<br>其实我们的网址<code>www.baidu.com</code>在配置当中应该是<code>www.baidu.com.</code>(最后多了一个.)。一般我们在浏览器里输入的时候都会省略这个<code>.</code>。</p>
<h3 id="域的划分"><a href="#域的划分" class="headerlink" title="域的划分"></a>域的划分</h3><p>根域下来就叫顶级域或一级域。有两种划分方式,一种是互联网刚兴起时划分的<code>com.</code>,<code>net.</code>等;一种是按国家划分的如<code>cn.</code>,<code>jp.</code>等,<strong>每个域都有域名服务器</strong>。</p>
<p>例如 <code>.com</code> 就是一个顶级域名;而 <code>www.baidu.com</code> 不是一个域名,它是<code>baidu.com</code>这个域名里的一个叫做<code>www</code>的主机。同理,在<code>a.www.baidu.com</code>里,主机名是<code>a</code>。</p>
<h2 id="解析过程"><a href="#解析过程" class="headerlink" title="解析过程"></a>解析过程</h2><p>具体过程如下:</p>
<ol>
<li>查询本地域名服务器(10.1.1.1),查找本地缓存中是否有<code>www.baidu.com</code>的IP地址。如果在缓存中找到,则跳到<code>#6</code>,没有找到则进行下一步;</li>
<li>在根域名服务器(.)中查询 -> 返回COM顶级域名服务器的IP</li>
<li>在COM顶级域名服务器(.com)中查询 -> 返回baidu.com二级域名服务器(.baidu.com)的IP</li>
<li>在baidu.com二级域名服务器(.baidu.com)中查询 -> 返回主机名为<code>www</code>的服务器的IP</li>
<li>将域名及对应IP存入缓存中</li>
<li>获得域名对应IP地址</li>
</ol>
<h1 id="TCP-连接"><a href="#TCP-连接" class="headerlink" title="TCP 连接"></a>TCP 连接</h1><p>获得目标服务器的<strong>IP地址</strong>和<strong>端口号</strong>之后,客户端和服务器之间将建立一条TCP/IP连接。<br>TCP是面向连接的、无差错的、按序传输的。</p>
<h2 id="三次握手"><a href="#三次握手" class="headerlink" title="三次握手"></a>三次握手</h2><p>在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。</p>
<p>第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;</p>
<p>第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器 进入SYN_RECV状态;</p>
<p>第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据.</p>
<p><img src="http://upload-images.jianshu.io/upload_images/533518-020ada1863d368a5.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="TCP/IP三次握手图解"></p>
<h2 id="四次挥手"><a href="#四次挥手" class="headerlink" title="四次挥手"></a>四次挥手</h2><p>由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。</p>
<ol>
<li>客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送(报文段4)。</li>
<li>服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。</li>
<li>服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)。</li>
<li>客户端A发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。</li>
</ol>
<p><img src="http://upload-images.jianshu.io/upload_images/533518-2e624cf66132b589.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="TCP/IP四次挥手图解"></p>
<h1 id="HTTP协议"><a href="#HTTP协议" class="headerlink" title="HTTP协议"></a>HTTP协议</h1><p>TCP/IP连接建立之后,浏览器会向服务器发送一条HTTP请求报文 -> 服务器向浏览器返回一条相应报文 -> 关闭连接</p>
<h2 id="报文"><a href="#报文" class="headerlink" title="报文"></a>报文</h2><p>报文可分为两类: <strong>请求报文</strong>和<strong>相应报文</strong>,它们都由三部分组成</p>
<ul>
<li>起始行(对报文进行描述)</li>
<li>首部块(包含属性)</li>
<li>主体部分(包含数据-可选)</li>
</ul>
<h3 id="起始行"><a href="#起始行" class="headerlink" title="起始行"></a>起始行</h3><ul>
<li>请求报文起始行</li>
<li>相应报文起始行</li>
</ul>
<p>请求报文起始行:<method> <request-url> <version><br>相应报文起始行:<version> <status-code> <reason-phrase></reason-phrase></status-code></version></version></request-url></method></p>
<h3 id="首部块"><a href="#首部块" class="headerlink" title="首部块"></a>首部块</h3><ul>
<li>通用首部</li>
<li>请求首部</li>
<li>响应首部</li>
<li>实体首部</li>
<li>扩展首部</li>
</ul>
<h3 id="主体部分"><a href="#主体部分" class="headerlink" title="主体部分"></a>主体部分</h3><p>是HTTP报文的负荷,也就是HTTP要传输的内容</p>
<h2 id="连接方式"><a href="#连接方式" class="headerlink" title="连接方式"></a>连接方式</h2><ul>
<li>并行连接</li>
<li>持久连接</li>
</ul>
<p>通常使用 少量并行+持久 的连接方式</p>
<p>HTTP/1.0 通过客户端设置请求首部 Connection: keep-alive</p>
<p>HTTP/1.1 默认持久连接,可以设置 Connection: close 关闭(默认持久连接并不代表连接会永远持续下去)</p>
<h1 id="页面渲染"><a href="#页面渲染" class="headerlink" title="页面渲染"></a>页面渲染</h1><ul>
<li>HTML代码转化成DOM</li>
<li>CSS代码转化成CSSOM(CSS Object Model)</li>
<li>结合DOM和CSSOM,生成一棵渲染树(包含每个节点的视觉信息)</li>
<li>生成布局(layout),即将所有渲染树的所有节点进行平面合成</li>
<li>将布局绘制(paint)在屏幕上</li>
</ul>
<h2 id="重排"><a href="#重排" class="headerlink" title="重排"></a>重排</h2><p>发生条件:</p>
<ul>
<li>页面初始化</li>
<li>插入/删除节点</li>
<li>改变元素尺寸(width/height/margin/padding/border 等等)</li>
<li>浏览器窗口变化</li>
<li>元素位置改变</li>
</ul>
<h2 id="重绘"><a href="#重绘" class="headerlink" title="重绘"></a>重绘</h2><p>发生条件:</p>
<ul>
<li>重排后必有重绘</li>
<li>color</li>
<li>visibility</li>
</ul>
<p>参考:<br><a href="https://juejin.im/entry/57f10284da2f60004f5f2e5e" target="_blank" rel="external">前端经典面试题: 从输入 URL 到页面加载发生了什么?</a><br><a href="http://blog.chinaunix.net/uid-28216282-id-3757849.html" target="_blank" rel="external">DNS解析过程详解</a><br><a href="http://www.techug.com/post/https-ssl-tls.html" target="_blank" rel="external">聊聊HTTPS和SSL/TLS协议</a><br><a href="http://www.jianshu.com/p/092705233d37" target="_blank" rel="external">TCP三次握手 四次挥手全过程</a><br><a href="http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html" target="_blank" rel="external">网页性能管理详解</a></p>
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://glassysky.github.io/2017/03/14/cross-domain-methods/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tianhao Cui">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="前端学习之路">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/03/14/cross-domain-methods/" itemprop="url">
跨域请求方法总结
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-03-14T23:32:04+08:00">
2017-03-14
</time>
</span>
<span class="post-comments-count">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-comment-o"></i>
</span>
<a href="/2017/03/14/cross-domain-methods/#comments" itemprop="discussionUrl">
<span class="post-comments-count disqus-comment-count" data-disqus-identifier="2017/03/14/cross-domain-methods/" itemprop="commentCount"></span>
</a>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="同源策略"><a href="#同源策略" class="headerlink" title="同源策略"></a>同源策略</h2><h3 id="含义"><a href="#含义" class="headerlink" title="含义"></a>含义</h3><p>起初的含义是指,A网页设置的Cookie,B网页不能打开,除非这两个网页”同源”。所谓”同源”是指一下几个相同:</p>
<ul>
<li>协议相同(http,https,http2等)</li>
<li>域名相同(www.example.com)</li>
<li>端口相同(www.example.com:3000)</li>
</ul>
<h3 id="目的"><a href="#目的" class="headerlink" title="目的"></a>目的</h3><p>同源策略的出现时为了保护用户信息的安全。如果用户登录了A网站,又去浏览其他网站。如果其他网站可以获取到A网站的Cookie,重要用户信息可能就会泄露。</p>
<p>通过XHR实现Ajax的一个主要限制,来自于跨域安全策略。这种安全策略可以防止某些恶意行为,但合理的实现跨域请求对某些应用来说也是非常必要的。</p>
<h3 id="限制范围"><a href="#限制范围" class="headerlink" title="限制范围"></a>限制范围</h3><p>目前,如果非同源,共有三种行为受到限制:</p>
<ul>
<li>Cookie、LocalStorage 和 IndexedDB 无法获取</li>
<li>DOM 无法获得</li>
<li>AJAX 请求不能发送</li>
</ul>
<h2 id="规避方法"><a href="#规避方法" class="headerlink" title="规避方法"></a>规避方法</h2><p>我们首先构建出一个跨域的环境方便测试:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">├── client</div><div class="line">│ ├── app.js</div><div class="line">│ ├── package.json</div><div class="line">│ └── public</div><div class="line">│ └── src</div><div class="line">│ ├── index.html</div><div class="line">│ └── index.js</div><div class="line">└── server</div><div class="line"> ├── app.js</div><div class="line"> ├── package.json</div><div class="line"> └── public</div><div class="line"> └── src</div></pre></td></tr></table></figure>
<ul>
<li>通过<code>express</code>分别在3000和4000端口启动两个服务</li>
<li>默认server为3000端口、client在4000端口</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// /server/app.js</span></div><div class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</div><div class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</div><div class="line"><span class="keyword">var</span> app = express()</div><div class="line"></div><div class="line">app.use(<span class="string">'/static'</span>, express.static(path.join(__dirname, <span class="string">'public'</span>)))</div><div class="line"></div><div class="line">app.get(<span class="string">'/'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</div><div class="line"> res.send(<span class="string">'hello world'</span>);</div><div class="line">})</div><div class="line"></div><div class="line">app.listen(<span class="number">3000</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'server app listening on port 3000!'</span>)</div><div class="line">})</div></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// /client/app.js</span></div><div class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</div><div class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</div><div class="line"><span class="keyword">var</span> app = express()</div><div class="line"></div><div class="line">app.use(<span class="string">'/static'</span>, express.static(path.join(__dirname, <span class="string">'public'</span>)))</div><div class="line"></div><div class="line">app.get(<span class="string">'/'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</div><div class="line"> res.send(<span class="string">'client'</span>)</div><div class="line">})</div><div class="line"></div><div class="line">app.listen(<span class="number">4000</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'client app listening on port 4000!'</span>)</div><div class="line">})</div></pre></td></tr></table></figure>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment"><!--/client/public/src/index.html--></span></div><div class="line"><span class="meta"><!DOCTYPE html></span></div><div class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></div><div class="line"><span class="tag"><<span class="name">head</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></div><div class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"ie=edge"</span>></span></div><div class="line"> <span class="tag"><<span class="name">title</span>></span>Document<span class="tag"></<span class="name">title</span>></span></div><div class="line"><span class="tag"></<span class="name">head</span>></span></div><div class="line"><span class="tag"><<span class="name">body</span>></span></div><div class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"./index.js"</span>></span><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></div><div class="line"><span class="tag"></<span class="name">body</span>></span></div><div class="line"><span class="tag"></<span class="name">html</span>></span></div></pre></td></tr></table></figure>
<p>环境搭建成功后,在4000端口上的js通过Ajax向3000端口的服务发送请求,就会触发跨域安全策略。</p>
<p>下面就介绍几种CORS(Cross-Origin Resource Sharing)的方法:</p>
<h3 id="原生支持"><a href="#原生支持" class="headerlink" title="原生支持"></a>原生支持</h3><p>Firefox 3.5+、Safari 4+、Chrome等主流浏览器都通过XMLHttpRequest对象实现了CORS的原生支持。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// /client/public/src/index.js</span></div><div class="line"><span class="keyword">var</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</div><div class="line"></div><div class="line">xhr.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">if</span> (xhr.readyState === <span class="number">4</span>) {</div><div class="line"> <span class="keyword">if</span> (xhr.status >= <span class="number">200</span> && xhr.status < <span class="number">300</span> || xhr === <span class="number">304</span>) {</div><div class="line"> <span class="built_in">console</span>.log(xhr.responseText)</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'fail'</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>同时需要对接口进行设置。通过设置<code>Access-Control-Allow-Origin</code>头可以决定来自哪些域的请求可以被实现。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// /server/app.js</span></div><div class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</div><div class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</div><div class="line"><span class="keyword">var</span> app = express()</div><div class="line"></div><div class="line">app.use(<span class="string">'/static'</span>, express.static(path.join(__dirname, <span class="string">'public'</span>)))</div><div class="line"></div><div class="line">app.get(<span class="string">'/'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</div><div class="line"> + res.header(<span class="string">'Access-Control-Allow-Origin'</span>, <span class="string">'http://localhost:4000'</span>);</div><div class="line"> res.send(<span class="string">'hello world'</span>);</div><div class="line">})</div><div class="line"></div><div class="line">app.listen(<span class="number">3000</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'server app listening on port 3000!'</span>)</div><div class="line">})</div></pre></td></tr></table></figure>
<p>设置成功后重启服务,我们可以看到这时的跨域请求已经发送成功,在client的index.html页面中已经可以看到请求得到的内容</p>
<h3 id="图像Ping"><a href="#图像Ping" class="headerlink" title="图像Ping"></a>图像Ping</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// /client/public/src/index.js</span></div><div class="line"><span class="keyword">var</span> img = <span class="keyword">new</span> Image();</div><div class="line">img.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'done'</span>);</div><div class="line">}</div><div class="line">img.onerror = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'fail'</span>);</div><div class="line">}</div><div class="line">img.src = <span class="string">'http://localhost:3000'</span>;</div></pre></td></tr></table></figure>
<p>通过创建一个<code>img</code>标签,并且设置对应的<code>src</code>,可以向目标url发送请求,但是这种方法无法收到返回的数据,只能发送<code>GET</code>请求。由于无法得到返回的数据,我们稍微改一下接口的代码</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// server/app.js</span></div><div class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</div><div class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</div><div class="line"><span class="keyword">var</span> app = express()</div><div class="line"></div><div class="line">app.use(<span class="string">'/static'</span>, express.static(path.join(__dirname, <span class="string">'public'</span>)))</div><div class="line"></div><div class="line">app.get(<span class="string">'/'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</div><div class="line"> - res.send(<span class="string">'hello world'</span>);</div><div class="line"> + <span class="built_in">console</span>.log(<span class="string">'receive get request'</span>);</div><div class="line">})</div><div class="line"></div><div class="line">app.listen(<span class="number">3000</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'server app listening on port 3000!'</span>)</div><div class="line">})</div></pre></td></tr></table></figure>
<p>这样我们通过命令行的输出就能够判断<code>http://localhost:3000</code>是否收到了请求。</p>
<p>刷新index.html页面后,我们可以看到控制台输出了<code>fail</code>,即图片加载失败,但是命令行会输出<code>receive get request</code>,即已经收到了通过图像Ping发送的<code>GET</code>请求。</p>
<h3 id="JSONP"><a href="#JSONP" class="headerlink" title="JSONP"></a>JSONP</h3><p>顾名思义 JSONP = JSON + Padding</p>
<p>不理解没关系,看下面的例子就知道了</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// /client/public/src/index.js</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">handleResponse</span> (<span class="params">response</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(response.test);</div><div class="line">}</div><div class="line"><span class="keyword">var</span> script = <span class="built_in">document</span>.createElement(<span class="string">'script'</span>);</div><div class="line">script.src = <span class="string">'http://localhost:3000/?callback=handleResponse'</span>;</div><div class="line"><span class="built_in">document</span>.body.insertBefore(script, <span class="built_in">document</span>.body.firstChild);</div></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// /server/app.js</span></div><div class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</div><div class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</div><div class="line"><span class="keyword">var</span> app = express()</div><div class="line"></div><div class="line">app.use(<span class="string">'/static'</span>, express.static(path.join(__dirname, <span class="string">'public'</span>)))</div><div class="line"></div><div class="line">app.get(<span class="string">'/'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</div><div class="line"> - res.send(<span class="string">'hello world'</span>);</div><div class="line"> + res.send(req.query.callback + <span class="string">'('</span> + <span class="string">'{test: 1}'</span> + <span class="string">')'</span>);</div><div class="line">})</div><div class="line"></div><div class="line">app.listen(<span class="number">3000</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'server app listening on port 3000!'</span>)</div><div class="line">})</div></pre></td></tr></table></figure>
<p>不难看出,服务端接口此时返回一个字符串,这个字符串的格式为<code>callback({test: 1})</code>,即用一个函数包含了一个JSON,这就是JSONP名字的由来。客户端收到这个字符串后,会对其进行调用。注意这里的<code>callback</code>就是作为请求的参数传给接口的值,因此这个参数要和自己定义的回调函数同名。</p>
<p>刷新页面重新请求时,控制台打印出<code>1</code>。这是因为返回的<code>handleResponse({test: 1})</code>字符串执行后,相当于把JSON格式的数据作为参数,这样在函数内部就能获取到了。</p>
<p>如果实在不理解的话,可以直接访问这个<a href="http://freegeoip.net/json/?callback=handleResponse" target="_blank" rel="external">链接</a>,它会返回一个JSONP字符串。</p>
<p>部分选自<a href="http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html?20170316133323#comment-last" target="_blank" rel="external">这篇博客</a></p>
<p><strong>转载请注明出处</strong></p>
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://glassysky.github.io/2017/03/13/understanding-HTML5-mobile-application-development/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tianhao Cui">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="前端学习之路">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/03/13/understanding-HTML5-mobile-application-development/" itemprop="url">
理解HTML5移动端应用开发
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-03-13T19:11:37+08:00">
2017-03-13
</time>
</span>
<span class="post-comments-count">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-comment-o"></i>
</span>
<a href="/2017/03/13/understanding-HTML5-mobile-application-development/#comments" itemprop="discussionUrl">
<span class="post-comments-count disqus-comment-count" data-disqus-identifier="2017/03/13/understanding-HTML5-mobile-application-development/" itemprop="commentCount"></span>
</a>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>开发者们可以通过HTML5简化移动应用开发的过程。HTML5移动端应用也伴随着很多问题,其中就包括性能无法与原生应用所匹敌。</p>
<p>许多组织开始转向HTML5的移动应用开发,以至于使应用的开发过程流水化,并且能够降低花销和复杂度。</p>
<p>在正确的环境下,HTML5应用能够提供给企业一个简单的替换,从而不需要进行原生应用的开发,特别是当越来越多的HTML5开发框架出现的时候。任何组织在跨平台实现移动端应用的时候,都至少应该考虑HTML5。</p>
<p>然而,HTML5应用并不是毫无挑战的。他们带来了性能和安全上的关注,并且在数据获取以及原生应用程序的接口上引发了问题。</p>
<h2 id="什么是HTML5应用?"><a href="#什么是HTML5应用?" class="headerlink" title="什么是HTML5应用?"></a>什么是HTML5应用?</h2><p>HTML5应用提供了”写一次代码,就能在任何地方运行”的移动端应用发展。有一系列移动设备优化的web页面,他们为视频、音频数据的推流,对图形和动画的处理、以及离线的支持提供了高级功能。他们也添加了语义化的元素,表单的控制和多媒体控件,而且伴随了大量的新API,它们提供了定位服务、拖动操作、本地应用缓存等等。</p>
<p>HTML5很少单独工作。更多的HTML5应用集成了CSS和JavaScript,分别定义了HTML组件如何在浏览器中渲染和对文本、对象、数组进行操作的API。许多主流的浏览器都支持这些技术,这使得实现基于web跨设备的应用的实现成为可能。</p>
<h2 id="HTML5移动端应用程序开发是如何工作的"><a href="#HTML5移动端应用程序开发是如何工作的" class="headerlink" title="HTML5移动端应用程序开发是如何工作的"></a>HTML5移动端应用程序开发是如何工作的</h2><p>HTML5移动端应用程序的开发框架工具包包括CSS库和JavaScript文件,开发者们可以将它们包含在HTML中。框架解决了由构建HTML5应用而引发的诸多问题。</p>
<p>例如,一个好的框架能够巧妙处理并重复应用文档对象模型(DOM)去提升性能。框架也使实现依赖于JavaScript的行为实现更加容易,例如屏幕转换和列表滑动。总的来说,这使得新手去创建一个HTML5应用变得更加容易了,因为许多开发的细节已经被处理过。</p>
<p>所有的框架都不相同,IT管理者必须在决定使用哪一个之前做足功课。每个框架都有自己的局限性和问题,他们经常会在应用变得更加复杂的时候被暴露出来。</p>
<p>在移动设备和浏览器之间的微小差别也影响了程序在不同环境中的表现。结果,开发者有时候会基于他们共有的特性进行构建,以此来确保跨平台编程,这些程序有可能会有损用户体验。尽管存在潜在的问题,有适当训练和经验的开发者在开发安全、基于web的高性能应用时应该没有问题。</p>
<h2 id="在构建HTML5应用的时候应该时刻注意的问题"><a href="#在构建HTML5应用的时候应该时刻注意的问题" class="headerlink" title="在构建HTML5应用的时候应该时刻注意的问题"></a>在构建HTML5应用的时候应该时刻注意的问题</h2><p>内存和性能限制是在HTML5移动应用开发时要关注的重点。开发者不仅要小心控制页面的流量,也要注意DOM的实现。对象模型越复杂,导航所需的时间越长。</p>
<p>综上,开发者应该时刻关注用户体验。为了确保用户体验的质量,HTML5移动端应用应该是轻量级的,以至于执行不会出现延迟。另外,管理者需要在多种平台上测试对应用进行测试,解决不同种类的设备、屏幕大小、浏览器和操作系统的问题。大多数应用需要支持离线操作以解决连接中断的问题。</p>
<h2 id="HTML5应用-VS-原生应用"><a href="#HTML5应用-VS-原生应用" class="headerlink" title="HTML5应用 VS 原生应用"></a>HTML5应用 VS 原生应用</h2><p>不像HTML5应用,原生的应用是基于应用运行平台上的原生语言。然而在原生应用中存在着挑战,每个平台都需要自己的应用,有一些平台需要一个应用的不同版本去支持不同平台上的不同设备。</p>
<p>HTML5是平台无关的。只要用户通过支持各种语言能力的浏览器获取到应用,应用在不同平台上也应该有相同的表现。另外,管理员只有一个代码库去维护和只要一个使用HTML5部署的包。他们能够分配bug修复并且在必要的时候可以随时更新,甚至与他们的用户进行性能在线测试,而不存在原生应用中的分配难题。</p>
<p>尽管HTML5已经走了很长一段路了,但是它仍然不能够完全实现所有的原生功能,例如多点触控手势;以及与设备内置组件的无缝结合,比如GPS。原生应用通常也会在性能上略胜一筹,因为他们是一部分机器指令。如果设计不当,HTML5应用可能会有一段很长的加载时间。</p>
<p>另外,HTML5应用受制于所有Web应用的风险。开发者必须对抗网络攻击,不正确的API使用以及通过Wi-Fi、蓝牙或者文本信息获取到设备的恶意代码的攻击。</p>
<h2 id="将HTML5和原生应用相结合"><a href="#将HTML5和原生应用相结合" class="headerlink" title="将HTML5和原生应用相结合"></a>将HTML5和原生应用相结合</h2><p>一个混合的移动端应用是一个HTML5应用运行在原生程序的容器中。应用使用设备的浏览器去渲染本地的HTML页面,当连接到设备的时候,功能就像一个原生应用。混合应用能够获取到像日历和联系人这样的功能,同时也有单个代码库能够嵌入到多个平台的优势。事实上,混合应用基本上能够做到原生应用能够做到的任何事情,包括支持离线操作和多点触控手势。一个混合应用甚至能够在外观和感觉上都模仿原生应用。然而,混合应用仍然不能表现得像原生应用一样,因为存在额外的抽象层。</p>
<p>已经出现了像PhoneGap这样的平台去实现混合移动应用。这个框架使得在开发移动端应用的时候使用HTML、CSS、JavaScript成为可能,同时也处理好了跨平台发布混合应用的细节。</p>
<p><a href="http://searchmobilecomputing.techtarget.com/feature/Understanding-HTML5-mobile-application-development" target="_blank" rel="external">原文地址</a></p>
</div>
<div>
</div>
<div>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
<link itemprop="mainEntityOfPage" href="http://glassysky.github.io/2017/03/12/4-javascript-design-patterns-you-should-know/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Tianhao Cui">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="前端学习之路">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2017/03/12/4-javascript-design-patterns-you-should-know/" itemprop="url">
你应该知道的4种JavaScript设计模式
</a>
</h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2017-03-12T15:39:51+08:00">
2017-03-12
</time>
</span>
<span class="post-comments-count">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-comment-o"></i>
</span>
<a href="/2017/03/12/4-javascript-design-patterns-you-should-know/#comments" itemprop="discussionUrl">
<span class="post-comments-count disqus-comment-count" data-disqus-identifier="2017/03/12/4-javascript-design-patterns-you-should-know/" itemprop="commentCount"></span>
</a>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">