-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
2452 lines (2155 loc) · 111 KB
/
index.html
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>
<head>
<meta charset="utf-8">
<title>复苏的奈文摩尔️</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:type" content="website">
<meta property="og:title" content="复苏的奈文摩尔️">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="复苏的奈文摩尔️">
<meta property="og:locale" content="default">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="复苏的奈文摩尔️">
<link rel="alternate" href="/atom.xml" title="复苏的奈文摩尔️" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/" id="logo">复苏的奈文摩尔️</a>
</h1>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
</nav>
<nav id="sub-nav">
<a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
<a id="nav-search-btn" class="nav-icon" title="Search"></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="http://yoursite.com"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-css" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/04/27/css/" class="article-date">
<time datetime="2018-04-27T06:24:20.000Z" itemprop="datePublished">2018-04-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/04/27/css/">关于css,你应该知道的</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p><a href="https://github.com/kamranahmedse/developer-roadmap" target="_blank" rel="noopener">https://github.com/kamranahmedse/developer-roadmap</a></p>
<h2 id="css预编译"><a href="#css预编译" class="headerlink" title="css预编译"></a>css预编译</h2><h3 id="sass"><a href="#sass" class="headerlink" title="sass"></a>sass</h3><h4 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h4><p>1、文件后缀名<br>.sass:不使用大括号和分号<br>.scss:和css格式接近,使用大括号和分号<br> `</p>
<pre><code>//文件后缀名为sass的语法
body
background: #eee
font-size:12px
p
background: #0982c1
//文件后缀名为scss的语法
body {
background: #eee;
font-size:12px;
}
p{
background: #0982c1;
} `
</code></pre><p>2、导入<br>通过@import来导入,导入的sass文件会被编程合并到一个css文件,但是如果在sass中导入css文件,效果和css的导入样式一样,会保留以@import形式存在。sass文件导入可以忽略后缀名。一般基础的文件命名方法以_开头,如_mixin.scssz。这种文件在导入的时候可以不写下划线,@import “mixin”。(顺便提一句,尽量少用css的@import因为它是在引用它的css文件下载下来以后,才会再去下载,用的多会严重影响浏览器并行加载css文件,影响页面渲染性能)<br> `</p>
<pre><code>//被导入的sass文件a.scss
//-------------------------------
body {
background: #eee;
}
//需要导入样式的sass文件b.scss
@import "reset.css";
@import "a";
p{
background: #0982c1;
}
//转译出来的b.css样式
@import "reset.css";
body {
background: #eee;
}
p{
background: #0982c1;
}
</code></pre><p> `<br>3、注释<br>多行注释:/**/<br>单行注释:// 单行注释不会被编译<br>4、变量<br>必须以$开头,后面紧跟变量名,变量名和变量值之间用:隔开,如果值后面加上!default则表示默认值。</p>
<p>普通变量:定义之后可以在全局范围内使用<br> `</p>
<pre><code>//sass style
//-------------------------------
$fontSize: 12px;
body{
font-size:$fontSize;
}
//css style
//-------------------------------
body{
font-size:12px;
}`
</code></pre><p>默认变量:仅需要在值后面加!default,一般是用来设置默认值,然后根据需求来覆盖的,覆盖的方式只要在默认变量之前重新声明下变量即可。<br> `</p>
<pre><code>//sass style
//-------------------------------
$baseLineHeight: 1.5 !default;
body{
line-height: $baseLineHeight;
}
//css style
//-------------------------------
body{
line-height:1.5;
}
//sass style
//-------------------------------
$baseLineHeight: 2;
$baseLineHeight: 1.5 !default;
body{
line-height: $baseLineHeight;
}
//css style
//-------------------------------
body{
line-height:2;
}`
</code></pre><p>特殊变量:一般我们定义的变量都为属性值,可直接使用,但是如果变量作为属性或在某些特殊情况下等则必须要以#{$variables}形式使用。<br> `</p>
<pre><code>//sass style
//-------------------------------
$borderDirection: top !default;
$baseFontSize: 12px !default;
$baseLineHeight: 1.5 !default;
//应用于class和属性
.border-#{$borderDirection}{
border-#{$borderDirection}:1px solid #ccc;
}
//应用于复杂的属性值
body{
font:#{$baseFontSize}/#{$baseLineHeight};
}
//css style
//-------------------------------
.border-top{
border-top:1px solid #ccc;
}
body {
font: 12px/1.5;
}`
</code></pre><p>多值变量:多值变量分为list和map类型,list类似js数组,map类似js对象</p>
<p>list数据可通过空格,逗号或小括号分隔多个值,可用nth($var,$index)取值。关于list数据操作还有很多其他函数如length($list),join($list1,$list2,[$separator]),append($list,$value,[$separator])等<br> `</p>
<pre><code>//定义一维数据
$px: 5px 10px 20px 30px;
//二维数据,相当于js中的二维数组
$px: 5px 10px, 20px 30px;
$px: (5px 10px) (20px 30px);
//使用
//sass style
//-------------------------------
$linkColor: #08c #333 !default;//第一个值为默认值,第二个鼠标滑过值
a{
color:nth($linkColor,1);
&:hover{
color:nth($linkColor,2);
}
}
//css style
//-------------------------------
a{
color:#08c;
}
a:hover{
color:#333;
}`
</code></pre><p>map数据以key和value成对出现<br> `</p>
<pre><code>//定义
$heading: (h1: 2em, h2: 1.5em, h3: 1.2em);
//使用
//sass style
//-------------------------------
$headings: (h1: 2em, h2: 1.5em, h3: 1.2em);
@each $header, $size in $headings {
#{$header} {
font-size: $size;
}
}
//css style
//-------------------------------
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.2em;
}
</code></pre><p>`</p>
<p>全局变量:在变量后面加!global现在用不上<br>目前的变量机制,在选择器中声明的变量会覆盖外面全局声明的变量(sass没有局部变量)</p>
<p>5、嵌套<br>选择器嵌套和属性的嵌套</p>
<p>选择器嵌套:我们最常用的方式<br> `</p>
<pre><code>#top_nav{
line-height: 40px;
text-transform: capitalize;
background-color:#333;
li{
float:left;
}
a{
display: block;
padding: 0 10px;
color: #fff;
&:hover{
color:#ddd;
}
}
}`
</code></pre><p>属性嵌套:<br> `</p>
<pre><code>//sass style
//-------------------------------
.fakeshadow {
border: {
style: solid;
left: {
width: 4px;
color: #888;
}
right: {
width: 2px;
color: #ccc;
}
}
}
//css style
//-------------------------------
.fakeshadow {
border-style: solid;
border-left-width: 4px;
border-left-color: #888;
border-right-width: 2px;
border-right-color: #ccc;
}`
</code></pre><p>6、@at-root</p>
<p>sass3.3.0中新增功能,用来跳出选择器嵌套的。默认所有的嵌套,继承所有上级选择器,但有了这个就可以跳出所有上级选择器。<br> `</p>
<pre><code>//sass style
//-------------------------------
//没有跳出
.parent-1 {
color:#f00;
.child {
width:100px;
}
}
//单个选择器跳出
.parent-2 {
color:#f00;
@at-root .child {
width:200px;
}
}
//多个选择器跳出
.parent-3 {
background:#f00;
@at-root {
.child1 {
width:300px;
}
.child2 {
width:400px;
}
}
}
//css style
//-------------------------------
.parent-1 {
color: #f00;
}
.parent-1 .child {
width: 100px;
}
.parent-2 {
color: #f00;
}
.child {
width: 200px;
}
.parent-3 {
background: #f00;
}
.child1 {
width: 300px;
}
.child2 {
width: 400px;
}`
</code></pre><p>7、混合(mixin)</p>
<p>sass中使用 @mixin声明混合,可以传递参数,参数名以$符号开始,多个参数以逗号分开,也可以给参数设置默认值。声明的 @mixin通过 @include来调用。<br> `</p>
<pre><code>//无参数mixin
//sass style
//-------------------------------
@mixin center-block {
margin-left:auto;
margin-right:auto;
}
.demo{
@include center-block;
}
//css style
//-------------------------------
.demo{
margin-left:auto;
margin-right:auto;
}
//有参数mixin
//sass style
//-------------------------------
@mixin opacity($opacity:50) {
opacity: $opacity / 100;
filter: alpha(opacity=$opacity);
}
//css style
//-------------------------------
.opacity{
@include opacity; //参数使用默认值
}
.opacity-80{
@include opacity(80); //传递参数
}`
</code></pre><p>8、继承</p>
<p>sass中,选择器继承可以让选择器继承另一个选择器的所有样式,并联合声明。使用选择器的继承,要使用关键词 @extend,后面紧跟需要继承的选择器。</p>
<pre><code>`
//sass style
//-------------------------------
</code></pre><p>h1{<br> border: 4px solid #ff9aa9;<br>}<br>.speaker{<br> @extend h1;<br> border-width: 2px;<br>}</p>
<p>//css style<br>//——————————-<br>h1,.speaker{<br> border: 4px solid #ff9aa9;<br>}<br>.speaker{<br> border-width: 2px;<br>}`</p>
<p>占位选择器 %<br>从sass 3.2.0以后就可以定义占位选择器 %。这种选择器的优势在于:如果不调用则不会有任何多余的css文件,避免了以前在一些基础的文件中预定义了很多基础的样式,然后实际应用中不管是否使用了 @extend去继承相应的样式,都会解析出来所有的样式。占位选择器以 %标识定义,通过 @extend调用。</p>
<pre><code>`
//sass style
//-------------------------------
%ir{
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
%clearfix{
@if $lte7 {
*zoom: 1;
}
&:before,
&:after {
content: "";
display: table;
font: 0/0 a;
}
&:after {
clear: both;
}
}
#header{
h1{
@extend %ir;
width:300px;
}
}
.ir{
@extend %ir;
}
//css style
//-------------------------------
#header h1,
.ir{
color: transparent;
text-shadow: none;
background-color: transparent;
border: 0;
}
#header h1{
width:300px;
}`
</code></pre><p>9、函数<br>sass定义了很多函数可供使用,当然你也可以自己定义函数,以@fuction开始,实际项目中我们使用最多的应该是颜色函数,而颜色函数中又以lighten减淡和darken加深为最,其调用方法为 lighten($color,$amount)和 darken($color,$amount),它们的第一个参数都是颜色值,第二个参数都是百分比。</p>
<p>10、运算<br>sass具有运算的特性,可以对数值型的Value(如:数字、颜色、变量等)进行加减乘除四则运算。请注意运算符前后请留一个空格,不然会出错。<br> `</p>
<pre><code>$baseFontSize: 14px !default;
$baseLineHeight: 1.5 !default;
$baseGap: $baseFontSize * $baseLineHeight !default;
$halfBaseGap: $baseGap / 2 !default;
$samllFontSize: $baseFontSize - 2px !default;
//grid
$_columns: 12 !default; // Total number of columns
$_column-width: 60px !default; // Width of a single column
$_gutter: 20px !default; // Width of the gutter
$_gridsystem-width: $_columns * ($_column-width + $_gutter); //grid system width`
</code></pre><p>11、条件判断及循环<br>@if判断<br>@if可一个条件单独使用,也可以和 @else结合多条件使用<br> `</p>
<pre><code>//sass style
//-------------------------------
$lte7: true;
$type: monster;
.ib{
display:inline-block;
@if $lte7 {
*display:inline;
*zoom:1;
}
}
p {
@if $type == ocean {
color: blue;
} @else if $type == matador {
color: red;
} @else if $type == monster {
color: green;
} @else {
color: black;
}
}
//css style
//-------------------------------
.ib{
display:inline-block;
*display:inline;
*zoom:1;
}
p {
color: green;
}`
</code></pre><p>三目判断<br>语法为: if($condition, $if_true, $if_false) 。三个参数分别表示:条件,条件为真的值,条件为假的值。<br> `</p>
<pre><code>if(true, 1px, 2px) => 1px
if(false, 1px, 2px) => 2px`
</code></pre><p>for循环<br>for循环有两种形式,分别为: @for $var from <start> through <end>和 @for $var from <start> to <end>。$i表示变量,start表示起始值,end表示结束值,这两个的区别是关键字through表示包括end这个数,而to则不包括end这个数。<br> `</end></start></end></start></p>
<pre><code>//sass style
//-------------------------------
@for $i from 1 through 3 {
.item-#{$i} { width: 2em * $i; }
}
//css style
//-------------------------------
.item-1 {
width: 2em;
}
.item-2 {
width: 4em;
}
.item-3 {
width: 6em;
}`
</code></pre><p>@each循环<br>语法为: @each $var in <list or="" map="">。其中 $var表示变量,而list和map表示list类型数据和map类型数据。sass 3.3.0新加入了多字段循环和map数据循环。</list></p>
<p>单个字段list数据循环<br> `</p>
<pre><code>//sass style
//-------------------------------
$animal-list: puma, sea-slug, egret, salamander;
@each $animal in $animal-list {
.#{$animal}-icon {
background-image: url('/images/#{$animal}.png');
}
}
//css style
//-------------------------------
.puma-icon {
background-image: url('/images/puma.png');
}
.sea-slug-icon {
background-image: url('/images/sea-slug.png');
}
.egret-icon {
background-image: url('/images/egret.png');
}
.salamander-icon {
background-image: url('/images/salamander.png');
}`
</code></pre><h2 id="less和sass的区别"><a href="#less和sass的区别" class="headerlink" title="less和sass的区别"></a>less和sass的区别</h2><p>1.编译环境不一样<br>Sass的安装需要Ruby环境,是在服务端处理的,而Less是需要引入less.js来处理Less代码输出css到浏览器,也可以在开发环节使用Less,然后编译成css文件,直接放到项目中,也有 Less.app、SimpleLess、CodeKit.app这样的工具,也有在线编译地址。</p>
<p>2.变量符不一样,Less是@,而Scss是$,而且变量的作用域也不一样。<br>3.输出设置,Less没有输出设置,Sass提供4中输出选项:nested, compact, compressed 和 expanded。<br>输出样式的风格可以有四种选择,默认为nested</p>
<p>nested:嵌套缩进的css代码<br>expanded:展开的多行css代码<br>compact:简洁格式的css代码<br>compressed:压缩后的css代码</p>
<p>4.Sass支持条件语句,可以使用if{}else{},for{}循环等等。而Less不支持。</p>
<ol start="5">
<li>引用外部CSS文件<br>scss引用的外部文件命名必须以_开头, 如下例所示:其中_test1.scss、_test2.scss、<em>test3.scss文件分别设置的h1 h2 h3。文件名如果以下划线</em>开头的话,Sass会认为该文件是一个引用文件,不会将其编译为css文件.<br>6.Sass和Less的工具库不同</li>
</ol>
<p>相同点:两者都是CSS预处理器,都具有相同的功能,可以帮助我们快速编译代码,帮助我们更好的维护我们的样式代码或者说维护项目吧。不同点:语法规则不同,当然功能或许略有差别<br>选择Sass的原因:<br>1、Sass也是成熟的CSS预处理器之一,而且有一个稳定,强大的团队在维护<br>2、Sass对于我来说参考的教程多<br>3、Sass有一些成熟稳定的框架,特别是Compass,新秀还有Foundation之类<br>4、还有一个原因是国外讨论Sass的同行要多于LESS</p>
<h3 id="比较sass、less、stylus"><a href="#比较sass、less、stylus" class="headerlink" title="比较sass、less、stylus"></a>比较sass、less、stylus</h3><p>1、变量:就像其他编程语言一样,免于多处修改。</p>
<p>Sass:使用「$」对变量进行声明,变量名和变量值使用冒号进行分割<br>Less:使用「@」对变量进行声明<br>Stylus:中声明变量没有任何限定,结尾的分号可有可无,但变量名和变量值之间必须要有『等号』。但需要注意的是,如果用“@”符号来声明变量,Stylus会进行编译,但不会赋值给变量。就是说,Stylus 不要使用『@』声明变量。Stylus 调用变量的方法和Less、Sass完全相同。</p>
<p>2、作用域:有了变量,就必须得有作用域进行管理。就想js一样,它会从局部作用域开始往上查找变量。</p>
<p>Sass:它的方式是三者中最差的,不存在全局变量的概念<br>Less:它的方式和js比较相似,逐级往上查找变量<br>Stylus:它的方式和Less比较相似,但是它和Sass一样更倾向于指令式查找</p>
<p>3、嵌套:对于css来说,有嵌套的写法无疑是完美的,更像是父子层级之间明确关系</p>
<p>三者在这处的处理都是一样的,使用「&」表示父元素</p>
<h2 id="postcss"><a href="#postcss" class="headerlink" title="postcss"></a>postcss</h2><p>对于css命名冲突的问题,由来已久,可以说我们前端开发人员,天天在苦思冥想,如何去优雅的解决这些问题。css并未像js一样出现了AMD、CMD和ES6 Module的模块化方案。</p>
<p>那么,回到问题,如何去解决呢?我们的前人也有提出过不同的方案:</p>
<p>Object-Oriented CSS<br>BEM<br>SMACSS<br>方案可以说是层出不穷,不乏有团队内部的解决方案。但是大多数都是一个共同点——为选择器增加前缀。写出来的class很长很复杂。<br>现在的网页开发,讲究的是组件化的思想,因此,急需要可行的css Module方式来完成网页组件的开发。自从2015年开始,国外就流行了CSS-in-JS(典型的代表,react的styled-components),还有一种就是CSS Module。<br>对于css,大家都知道,它是一门描述类语言,并不存在动态性。那么,要如何去形成module呢。我们可以先来看一个react使用postcss的例子:<br> `</p>
<pre><code>//example.css
.article {
font-size: 14px;
}
.title {
font-size: 20px;
}`
</code></pre><p>之后,将这些命名打乱<br> `</p>
<pre><code>.zxcvb{
font-size: 14px;
}
.zxcva{
font-size: 20px;
}`
</code></pre><p>将之命名对应的内容,放入到json文件中去<br> `</p>
<pre><code>{
"article": "zxcvb",
"title": "zxcva"
}`
</code></pre><p>之后,在js文件中运用<br> `</p>
<pre><code>import style from 'style.json';
class Example extends Component{
render() {
return (
<div classname={style.article}>
<div classname={style.title}></div>
</div>
)
}
}`
</code></pre><p>这样子,就描绘出了一副css module的原型。当然,我们不可能每次都需要手动去写这些东西。我们需要自动化的插件帮助我们完成这一个过程。之后,我们应该先来了解一下postCSS。<br>PostCSS是什么?或许,你会认为它是预处理器、或者后处理器等等。其实,它什么都不是。它可以理解为一种插件系统。<br>你可以在使用预处理器的情况下使用它,也可以在原生的css中使用它。它都是支持的,并且它具备着一个庞大的生态系统,例如你可能常用的Autoprefixer,就是PostCSS的一个非常受欢迎的插件,被Google, Shopify, Twitter, Bootstrap和CodePen等公司广泛使用。<br>接下来,我们来看一下PostCSS的配置:</p>
<p>这里我们使用webpack+postcss+postcss-loader+cssnext+postcss-import的组合。<br> `</p>
<pre><code>yarn add --dev webpack extract-text-webpack-plugin css-loader file-loader postcss postcss-loader postcss-cssnext postcss-import`
</code></pre><p>然后,我们配置一下webpack.config.js:<br> `</p>
<pre><code>const webpack = require('webpack');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
context: path.resolve(__dirname, 'src'),
entry: {
app: './app.js';
},
module: {
loaders: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: [
{
loader: 'css-loader',
options: { importLoaders: 1 },
},
'postcss-loader',
],
}),
},
],
},
output: {
path: path.resolve(__dirname, 'dist/assets'),
},
plugins: [
new ExtractTextPlugin('[name].bundle.css'),
],
};`
</code></pre><p>然后在根目录下配置postcss.config.js<br> `</p>
<pre><code>module.exports = {
plugins: {
'postcss-import': {},
'postcss-cssnext': {
browsers: ['last 2 versions', '> 5%'],
},
},
};`
</code></pre><p>之后,就可以在开发中使用cssnext的特性了<br> `</p>
<pre><code>/* Shared */
@import "shared/colors.css";
@import "shared/typography.css";
/* Components */
@import "components/Article.css";
/* shared/colors.css */
:root {
--color-black: rgb(0,0,0);
--color-blue: #32c7ff;
}
/* shared/typography.css */
:root {
--font-text: "FF DIN", sans-serif;
--font-weight: 300;
--line-height: 1.5;
}
/* components/Article.css */
.article {
font-size: 14px;
& a {
color: var(--color-blue);
}
& p {
color: var(--color-black);
font-family: var(--font-text);
font-weight: var(--font-weight);
line-height: var(--line-height);
}
@media (width > 600px) {
max-width: 30em;
}
}`
</code></pre><p>个人感觉css module是比较好的处理css打包的解决方案,给我们开发带来很大的便利,结合sass等css预处理,高效的完成开发工作。</p>
<p>另外关于css结构可以关注一下下面几个关键词</p>
<h3 id="BEM、OOCSS、SMACSS、SUITCSS、Atomic"><a href="#BEM、OOCSS、SMACSS、SUITCSS、Atomic" class="headerlink" title="BEM、OOCSS、SMACSS、SUITCSS、Atomic"></a>BEM、OOCSS、SMACSS、SUITCSS、Atomic</h3>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2018/04/27/css/" data-id="cjghknvxa0000n4vwfcwmygw7" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-redux" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2018/04/25/redux/" class="article-date">
<time datetime="2018-04-25T08:58:48.000Z" itemprop="datePublished">2018-04-25</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/04/25/redux/">flux->redux->redux-saga->dva说明</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h2 id="flux"><a href="#flux" class="headerlink" title="flux"></a>flux</h2><p>简单说,Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC 架构是同一类东西,但是更加简单和清晰<br>首先,Flux将一个应用分成四个部分。<br>View: 视图层<br>Action(动作):视图层发出的消息(比如mouseClick)<br>Dispatcher(派发器):用来接收Actions、执行回调函数<br>Store(数据层):用来存放应用的状态,一旦发生变动,就提醒Views要更新页面<br>Flux 的最大特点,就是数据的”单向流动”。<br>用户访问 View<br>View 发出用户的 Action<br>Dispatcher 收到 Action,要求 Store 进行相应的更新<br>Store 更新后,发出一个”change”事件<br>View 收到”change”事件后,更新页面</p>
<h2 id="redux"><a href="#redux" class="headerlink" title="redux"></a>redux</h2><h3 id="设计思想"><a href="#设计思想" class="headerlink" title="设计思想"></a>设计思想</h3><p>(1)Web 应用是一个状态机,视图与状态是一一对应的。</p>
<p>(2)所有的状态,保存在一个对象里面。</p>
<h3 id="API"><a href="#API" class="headerlink" title="API"></a>API</h3><p>1、Store</p>
<p>Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。</p>
<p>Redux 提供createStore这个函数,用来生成 Store。<br> `</p>
<pre><code>import { createStore } from 'redux';
const store = createStore(fn,initial_state,applyMiddleware());
//后面会知道这个fn就是reducer,initial_state是初始状态,applyMiddleware是Redux的原生方法,作用是将所有中间件组成一个数组,依次执行`
</code></pre><p>2、state<br>Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。</p>
<p>当前时刻的 State,可以通过store.getState()拿到。</p>
<pre><code>`
import { createStore } from 'redux';
const store = createStore(fn);
const state = store.getState();`
</code></pre><p>3、Action<br>state 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。</p>
<p>Action 是一个对象。其中的type属性是必须的,表示 Action 的名称。</p>
<pre><code>`
const action = {
type: 'ADD_TODO',
payload: 'Learn Redux'
};`
</code></pre><p>4、store.dispatch()<br>store.dispatch()是 View 发出 Action 的唯一方法。<br> `</p>
<pre><code>import { createStore } from 'redux';
const store = createStore(fn);
store.dispatch({
type: 'ADD_TODO',
payload: 'Learn Redux'
});`
</code></pre><p>5、Reducer</p>
<p>Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。</p>
<p>Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。</p>
<pre><code>`
const reducer = function (state, action) {
// ...
return new_state;
};
const defaultState = 0;
const reducer = (state = defaultState, action) => {
switch (action.type) {
case 'ADD':
return state + action.payload;
default:
return state;
}
};
const state = reducer(1, {
type: 'ADD',
payload: 2
});`
</code></pre><p>实际应用中,Reducer 函数不用像上面这样手动调用,store.dispatch方法会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。<br> `</p>
<pre><code>import { createStore } from 'redux';
const store = createStore(reducer);`
</code></pre><p>6、 store.subscribe()<br> Store 允许使用store.subscribe方法设置监听函数,一旦 State 发生变化,就自动执行这个函数。<br> `</p>
<pre><code>import { createStore } from 'redux';
const store = createStore(reducer);
store.subscribe(listener);//结合react,可以对render进行监听`
</code></pre><p>7、Store的实现</p>
<p>store提供了三个方法<br>store.getState()<br>store.dispatch()<br>store.subscribe()</p>
<p>简单实现原理<br> `</p>
<pre><code>const createStore = (reducer) => {
let state;
let listeners = [];
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(listener => listener());
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener);
}
};
dispatch({});
return { getState, dispatch, subscribe };
};`
</code></pre><p>以上简单说明了redux的工作原理,但是会发现一个问题,现在的action都是同步操作,那么redux如何处理异步的action,于是产生了中间件的概念,也就是对dispatch过程进行了处理,实现异步操作</p>
<p>异步操作的基本思路<br>同步操作只要发出一种 Action 即可,异步操作的差别是它要发出三种 Action<br>操作发起时的 Action<br>操作成功时的 Action<br>操作失败时的 Action</p>
<p>操作开始时,送出一个 Action,触发 State 更新为”正在操作”状态,View 重新渲染<br>操作结束后,再送出一个 Action,触发 State 更新为”操作结束”状态,View 再一次重新渲染</p>
<p>异步操作至少要送出两个 Action:用户触发第一个 Action,这个跟同步操作一样,没有问题;如何才能在操作结束时,系统自动送出第二个 Action 呢?</p>
<pre><code>`
const fetchPosts = postTitle => (dispatch, getState) => {
dispatch(requestPosts(postTitle));
return fetch(`/some/API/${postTitle}.json`)
.then(response => response.json())
.then(json => dispatch(receivePosts(postTitle, json)));
};
};
store.dispatch(fetchPosts('reactjs'));
上面代码中,fetchPosts是一个Action Creator(动作生成器),返回一个函数。这个函数执行后,先发出一个Action(requestPosts(postTitle)),然后进行异步操作。拿到结果后,先将结果转成 JSON 格式,然后再发出一个 Action( receivePosts(postTitle, json))。
</code></pre><p>(1)fetchPosts返回了一个函数,而普通的 Action Creator 默认返回一个对象。</p>
<p>(2)返回的函数的参数是dispatch和getState这两个 Redux 方法,普通的 Action Creator 的参数是 Action 的内容。</p>
<p>(3)在返回的函数之中,先发出一个 Action(requestPosts(postTitle)),表示操作开始。</p>
<p>(4)异步操作结束之后,再发出一个 Action(receivePosts(postTitle, json)),表示操作结束。</p>
<p>这样的处理,就解决了自动发送第二个 Action 的问题。但是,又带来了一个新的问题,Action 是由store.dispatch方法发送的。而store.dispatch方法正常情况下,参数只能是对象,不能是函数。<br>所以就需要使用中间件(redux-thunk),改造store.dispatch,使得后者可以接受函数作为参数。</p>
<p>因此,异步操作的第一种解决方案就是,写出一个返回函数的 Action Creator,然后使用redux-thunk中间件改造store.dispatch</p>
<p>既然 Action Creator 可以返回函数,当然也可以返回其他值。另一种异步操作的解决方案,就是让 Action Creator 返回一个 Promise 对象。</p>
<p>这就需要使用redux-promise中间件。</p>
<h3 id="react-redux"><a href="#react-redux" class="headerlink" title="react-redux"></a>react-redux</h3><p>React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。</p>
<p>UI 组件有以下几个特征。<br>只负责 UI 的呈现,不带有任何业务逻辑<br>没有状态(即不使用this.state这个变量)<br>所有数据都由参数(this.props)提供<br>不使用任何 Redux 的 API</p>
<p>容器组件的特征恰恰相反。<br>负责管理数据和业务逻辑,不负责 UI 的呈现<br>带有内部状态<br>使用 Redux 的 API</p>
<pre><code>`
import { connect } from 'react-redux'
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)`
</code></pre><p>mapStateToProps是一个函数。它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。<br> `</p>
<pre><code>const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}`
</code></pre><p>mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。</p>
<p>mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。</p>
<p>mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。</p>
<p>如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。<br> `</p>
<pre><code>const mapDispatchToProps = (
dispatch,
ownProps
) => {
return {
onClick: () => {
dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: ownProps.filter
});
}
};
}`
</code></pre><p>React-Redux 提供Provider组件,可以让容器组件拿到state。</p>
<pre><code>`
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp);
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)`
</code></pre><p>上面代码中,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了。</p>
<p>它的原理是React组件的context属性,请看源码。<br> `</p>
<pre><code>class Provider extends Component {
getChildContext() {
return {
store: this.props.store
};
}
render() {
return this.props.children;
}
}
Provider.childContextTypes = {
store: React.PropTypes.object
}`
</code></pre><h2 id="redux-saga"><a href="#redux-saga" class="headerlink" title="redux-saga"></a>redux-saga</h2><p>redux-saga的实质是类似redux-thunk的中间件,作用是为redux提供额外的功能<br>其次,我们都知道,在 reducers 中的所有操作都是同步的并且是纯粹的,即 reducer 都是纯函数,纯函数是指一个函数的返回结果只依赖于它的参数,并且在执行过程中不会对外部产生副作用,即给它传什么,就吐出什么。但是在实际的应用开发中,我们希望做一些异步的(如Ajax请求)且不纯粹的操作(如改变外部的状态),这些在函数式编程范式中被称为“副作用”。</p>
<p>Redux 的作者将这些副作用的处理通过提供中间件的方式让开发者自行选择进行实现。</p>
<p>redux-thunk 和 redux-saga 是 redux 应用中最常用的两种异步流处理方式。</p>
<h3 id="redux-thunk"><a href="#redux-thunk" class="headerlink" title="redux-thunk"></a>redux-thunk</h3><p>redux-thunk 的任务执行方式是从 UI 组件直接触发任务。redux-thunk 的主要思想是扩展 action,使得 action 从一个对象变成一个函数。<br> `</p>
<pre><code>// fetchUrl 返回一个 thunk
function fetchUrl(url) {
return (dispatch) => {
dispatch({
type: 'FETCH_REQUEST'
});
fetch(url).then(data => dispatch({
type: 'FETCH_SUCCESS',
data
}));
}
}
// 如果 thunk 中间件正在运行的话,我们可以 dispatch 上述函数如下:
dispatch(
fetchUrl(url)
):
</code></pre><p>`<br>redux-thunk 的缺点:<br>(1)action 虽然扩展了,但因此变得复杂,后期可维护性降低;<br>(2)thunks 内部测试逻辑比较困难,需要mock所有的触发函数;<br>(3)协调并发任务比较困难,当自己的 action 调用了别人的 action,别人的 action 发生改动,则需要自己主动修改;<br>(4)业务逻辑会散布在不同的地方:启动的模块,组件以及thunks内部。</p>
<h3 id="redux-saga-1"><a href="#redux-saga-1" class="headerlink" title="redux-saga"></a>redux-saga</h3><p>sages 采用 Generator 函数来 yield Effects(包含指令的文本对象)。Generator 函数的作用是可以暂停执行,再次执行的时候从上次暂停的地方继续执行。Effect 是一个简单的对象,该对象包含了一些给 middleware 解释执行的信息。你可以通过使用 effects API 如 fork,call,take,put,cancel 等来创建 Effect。<br>与 redux-thunk 不同的是,在 redux-saga 中,UI 组件自身从来不会触发任务,它们总是会 dispatch 一个 action 来通知在 UI 中哪些地方发生了改变,而不需要对 action 进行修改。redux-saga 将异步任务进行了集中处理,且方便测试。<br>所有的东西都必须被封装在 sagas 中。sagas 包含3个部分,用于联合执行任务:<br>1、worker saga<br>做所有的工作,如调用 API,进行异步请求,并且获得返回结果<br>2、watcher saga<br>监听被 dispatch 的 actions,当接收到 action 或者知道其被触发时,调用 worker saga 执行任务<br>3、root saga<br>立即启动 sagas 的唯一入口<br>具体使用姿势:<br> `</p>
<pre><code>class UserComponent extends React.Component {
...
onSomeButtonClicked() {
const { userId, dispatch } = this.props
dispatch({type: 'USER_FETCH_REQUESTED', payload: {userId}})
}
...
}
//sagas.js
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...'
// worker Saga: will be fired on USER_FETCH_REQUESTED actions
function* fetchUser(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
}
/*
Starts fetchUser on each dispatched `USER_FETCH_REQUESTED` action.
Allows concurrent fetches of user.
*/
function* mySaga() {
yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}
/*
Alternatively you may use takeLatest.
Does not allow concurrent fetches of user. If "USER_FETCH_REQUESTED" gets
dispatched while a fetch is already pending, that pending fetch is cancelled
and only the latest one will be run.
*/
function* mySaga() {
yield takeLatest("USER_FETCH_REQUESTED", fetchUser);
}
export default mySaga;
//main.js
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import mySaga from './sagas'
// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
// then run the saga
sagaMiddleware.run(mySaga)
// render the application
`
</code></pre><p>最后,总结一下 redux-saga 的优点:</p>
<p>(1)声明式 Effects:所有的操作以JavaScript对象的方式被 yield,并被 middleware 执行。使得在 saga 内部测试变得更加容易,可以通过简单地遍历 Generator 并在 yield 后的成功值上面做一个 deepEqual 测试。<br>(2)高级的异步控制流以及并发管理:保持 action 的简单纯粹,aciton 不再像原来那样五花八门,让人眼花缭乱。task 的模式使代码更加清晰。可以使用简单的同步方式描述异步流,并通过 fork 实现并发任务。<br>(3)架构上的优势:将所有的异步流程控制都移入到了 sagas,UI 组件不用执行业务逻辑,只需 dispatch action 就行,增强组件复用性。扩展性强。</p>
<h2 id="dva"><a href="#dva" class="headerlink" title="dva"></a>dva</h2><p>先考虑一下redux-saga的缺点<br>1、编辑成本高,需要在 reducer, saga, action 之间来回切换<br>2、不便于组织业务模型 (或者叫 domain model) 。比如我们写了一个 userlist 之后,要写一个 productlist,需要复制很多文件。<br>还有一些其他的:<br>saga 书写太复杂,每监听一个 action 都需要走 fork -> watcher -> worker 的流程<br>entry 书写麻烦<br>…<br>而 dva 正是用于解决这些问题。<br>他最核心的是提供了 app.model 方法,用于把 reducer, initialState, action, saga 封装到一起,比如:<br> `</p>
<pre><code>app.model({
namespace: 'products',
state: {
list: [],
loading: false,
},
subscriptions: [
function(dispatch) {
dispatch({type: 'products/query'});
},
],
effects: {
['products/query']: function*() {
yield call(delay(800));
yield put({
type: 'products/query/success',
payload: ['ant-tool', 'roof'],
});
},
},
reducers: {
['products/query'](state) {
return { ...state, loading: true, };
},
['products/query/success'](state, { payload }) {
return { ...state, loading: false, list: payload };
},
},
});`
</code></pre><p>在有 dva 之前,我们通常会创建 sagas/products.js, reducers/products.js 和 actions/products.js,然后在这些文件之间来回切换,使用dva以后可以将所有的逻辑操作在model中完成。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2018/04/25/redux/" data-id="cjghknvxs0005n4vw9n6d961i" class="article-share-link">Share</a>
</footer>
</div>