forked from Num142857/alili.tech
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.xml
3007 lines (2617 loc) · 330 KB
/
index.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
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
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Hello Alili</title>
<link>https://alili.tech/</link>
<description>Recent content on Hello Alili</description>
<generator>Hugo -- gohugo.io</generator>
<language>zh</language>
<lastBuildDate>Wed, 31 Mar 2021 22:30:05 +0000</lastBuildDate>
<atom:link href="https://alili.tech/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Mac 最小化所有应用快捷键</title>
<link>https://alili.tech/archive/ry80uk3igi/</link>
<pubDate>Wed, 31 Mar 2021 22:30:05 +0000</pubDate>
<guid>https://alili.tech/archive/ry80uk3igi/</guid>
<description>平时使用Mac 的时候,桌面太多的应用窗口.
一直以来没办法跟windows一样可以一键显示桌面.
研究了一下,在Mac上还是有办法做到一样的效果的.
Command+Option+H+M </description>
</item>
<item>
<title>2020年终有感</title>
<link>https://alili.tech/archive/pmzpd2410y/</link>
<pubDate>Thu, 04 Mar 2021 16:41:00 +0000</pubDate>
<guid>https://alili.tech/archive/pmzpd2410y/</guid>
<description>终于可以在这个关键的时间节点写出这篇文章了,虽然现在已经是2021年的3月了. 但是一直没有找到一个很好的机会可以将这一篇文章写出来.
因为疫情原因,我知道大家的2020年有太多讲不完的故事. 我也一样,这一年也是我成长关键的一年.
关于工作 因为去年对跨端方面有了一定的进展,公司组织架构调整之后,成立了轻应用小组. 专门做跨端方向的技术研究来支撑公司的业务发展.
一年下来,遇到的问题极多对于我来说压力也极大,时常因为一些很难解决的问题导致业务可能上不了线. 但是问题总是在上线之前找到解决方案.这种事情发生了太多,对于我个人来说,心也得到了非常大的历练. 一年下来,我的口头禅就变成了这个问题不大. 不仅给自己打气,作为团队里面bug兜底的男人,也不断给碰到问题的同学一些信心. 这可能是我前端生涯中碰到问题最多,最难的一年了.但是成果还是相当不错的. 我们在一年时间里,将我们的服务拓展到了8个端~ 在此期间,也不能说没有放弃的念头,因为碰到的疑难杂症越多,大家倒是信心满满~ 很多时候,大家可能觉着,就算自己解决不了,至少还有我兜着~ 我承认,其中肯定有赌的成分,但是如果时间期限是一年的话,我们赌赢了. 但是带来一个问题,因为疑难杂症的问题解决多了,成就感触发的越来越难.
但是在业务方面,虽然大家很努力,但是并没有实质性的进展.这让人很疲惫. 在疫情面前,大部分公司或多或少都有类似的问题. 就好像不管用什么办法,数据都像是死猪不怕开水烫一般,没有实质性的波澜~
这让大家都非常的疲惫,希望在下一个阶段可以重新找回自己.
关于自己 说到找回自己,我刚刚在想,我是否曾经得到过自己. 或者说自己对自己有了一定的误解. 对于程序员的自己来说,可能曾经每天都可以进入心流状态的我来说是怀念的. 进入心流就是一整天,下班的时候可能才会有一些烦恼的事情进入大脑,大部分时间都是一个亢奋又沉浸的世界.</description>
</item>
<item>
<title>使用Taro开发各端的顺序建议</title>
<link>https://alili.tech/archive/hm5dl5tsw3k/</link>
<pubDate>Wed, 16 Dec 2020 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/hm5dl5tsw3k/</guid>
<description>以最小成本开发Taro各端 现在公司里面的所有小程序,快应用,rn等等都在使用Taro在开发.
如果只兼容一端的话,使用Taro开发,没有任何问题.
但是又想一次开发,各端正常运行的话,在很多的细节上是要花一点心思的.
每一端有每一端的难.
开发难度排序 华为快应用 &gt; 快应用 &gt; React Native &gt; Swan小程序 &gt; 微信小程序 &gt; H5
关于快应用 在开发过程中,如果有涉及到快应用的业务,建议优先开发快应用.
快应用的布局标准跟js容器环境跟其他的小程序或者是H5有一定的差异.
并且华为快应用与其他快应用有很多莫名其妙的差异.
如果涉及到快应用,请谨慎对待开发时间.
目前看来,快应用的对UI布局的支持与小程序有一定的差异.虽然使用Taro的样式区分,快应用与小程序确实可以使用一套同样的代码同时跑在两端上.
但是在开发过程中,他们之间的差异有太多意想不到(快应用不符合W3C规范)的差异,建议UI布局相关的代码,各端单独布局.业务相关的可以使用一套代码. 就是布局样式独立,行为保持一致.
不然会花掉太多的布局差异抹平的工作.
关于React Native React Native在UI布局上也只是部分支持W3C标准.虽然布局上不能跟小程序H5一样灵活,
对比起快应用来,并不会多出一个要额外兼容的华为快应用.
目前Taro封装的React Native的特性与小程序是非常接近的, 如果不过多调用太多冷门的API,基本上还是可以独立开发完成大部分UI编程.
如果RN想要调用原生APP的组件,在目前开来会带来相当大的沟通成本.
在开发中本身ios与安卓就有一定的特性差异,放到RN里实现后又会带来其他的差异.
并且开发人员从一个人变成了三个人.
如果桥接的组件太过于复杂,开发人员可能会有一种还不如自己独立开发的抱怨.
这个时候多端一起开发,时间不一定会比独立开发要快.
关于 Swan小程序 Swan小程序与微信小程序已经非常接近了.
就我们现有业务来说除了wifi不能实现以外,其他的基本上都跟微信小程序保持了一致.
目前开发中,IDE的卡顿是Swan小程序最不好的开发体验.
迭代中,swan小程序的基础库兼容方面也做得不是很好,
好几次遇到莫名其妙的问题,都是因为基础库兼容不好导致的.</description>
</item>
<item>
<title>使用Taro开发的快应用如何优化体积</title>
<link>https://alili.tech/archive/n1j1l1fvzbb/</link>
<pubDate>Tue, 15 Dec 2020 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/n1j1l1fvzbb/</guid>
<description>快应用重复打包问题 使用Taro开发快应用,有一个问题绝对不能忽视,那就是体积问题.
因为快应用打包的特性(1080以下)多个页面里,如果重复应用了一个第三方库,那么这个库的代码会一起打到这个页面中,导致一样的代码会存在多个页面中.
因为目前1080版本的快应用没有全面铺开.如果你贸然升级自己快应用的最低平台版本号为1080. 你将会失去大量低版本的用户.
官方给出的解决方案 (1080以下版本) 当然快应用官方也意识到了这个问题,给出了最早期的解决方案.
那就是将多个页面会依赖到的代码,全部在快应用初始化的时候挂载到全局变量上面.
其他页面使用的时候,直接引用全局变量就可以了.
// 入口文件 import day from 'day'; const globalRef = Object.getPrototypeOf(global) || global; globalRef.day = day; // 页面使用 const globalRef = Object.getPrototypeOf(global) || global const day = globalRef.day day() 分析项目使用了哪些公共代码 重复打包的内容,npm依赖会占大头,也有可能是自己写的公共代码.
我们的项目用到了哪些npm依赖? Taro打包之后,dist/quickapp 下的构建产物会单独把所有项目能用到的npm包放到 src/npm/ 目录下.
公共代码分析 只要一个js被多个page引用,就必须会造成重复打包的问题.
使用Taro的 alias 特性解决重复打包的问题 alias 是应用路径别名的特性,他的实现原理很简单,在config文件中制定好路径替换规则.
在taro打包的时候,直接替换应用路径成为正确的应用路径就可以了.
{ '@src': 'src', '@plugin': 'src/plugin', '@components': 'src/components', } 如何使用alias解决我们的打包问题 既然 alias有替换应用路径的问题,再加上我们的第三方库只要挂载到全局变量中就可以让所有页面都可以使用到.
两个特性结合起来的解决方案就是这样的.
第一步,修改npm的alias 为什么要这样做,看到第二步你就会明白.</description>
</item>
<item>
<title>TensorFlow中的Tensor是什么?</title>
<link>https://alili.tech/archive/eujpibnlnp8/</link>
<pubDate>Fri, 18 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/eujpibnlnp8/</guid>
<description>Tensor(张量) “张量”一词最初由威廉·罗恩·哈密顿在1846年引入。对,就是那个发明四元数的哈密顿:
Tensor实际上就是一个多维数组(multidimensional array)
Tensor的目的是能够创造更高维度的矩阵、向量。
色彩的例子 彩色图像文件(RGB)一般都会处理成3-d tensor,每个2d array中的element表示一个像素,R代表Red,G代表Green,B代表Blue
多维数组 把三维张量画成一个立方体:
更高维的张量:
初始化一个向量 0维 tf.tensor(1).print(); 1维 tf.tensor([1, 2, 3, 4]).print(); // or tf.tensor1d([1, 2, 3]).print(); 2维 tf.tensor([[1, 2], [3, 4]]).print(); // or tf.tensor2d([[1, 2], [3, 4]]).print(); 3维 tf.tensor([[[1], [2]], [[3], [4]]]).print(); // or tf.tensor3d([[[1], [2]], [[3], [4]]]).print(); 4维 tf.tensor([[[[1], [2]], [[3], [4]]]]).print(); // or tf.tensor4d([[[[1], [2]], [[3], [4]]]]).print(); 5维 tf.tensor([[[[[1], [2]], [[3], [4]]]]]).</description>
</item>
<item>
<title>Mac系统开启Chrome 跟 Edge的网页强制暗黑模式</title>
<link>https://alili.tech/archive/xewn7qbng4a/</link>
<pubDate>Thu, 17 Sep 2020 22:30:05 +0000</pubDate>
<guid>https://alili.tech/archive/xewn7qbng4a/</guid>
<description>开启强制暗黑模式 Chrome 进入浏览器,地址栏输入:
chrome://flags/#enable-force-dark Edge 进入浏览器,地址栏输入:
edge://flags/#enable-force-dark 操作 将第一项 Disabled 改成 Enabled,然后重启浏览器
生效:</description>
</item>
<item>
<title>Mac系统如何关掉Chrome的跨域限制</title>
<link>https://alili.tech/archive/leqgfwbuko/</link>
<pubDate>Wed, 16 Sep 2020 22:30:05 +0000</pubDate>
<guid>https://alili.tech/archive/leqgfwbuko/</guid>
<description> 关掉Chrome 跨域限制 因为调试经常需要Chrome关掉跨域限制,网上的方法大多是windows的,
这里今天记录一下:
open -n -a /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \ --args --user-data-dir=&quot;/tmp/chrome_dev_test&quot; --disable-web-security </description>
</item>
<item>
<title>数学篇 - 数据结构丶编程语句丶基础算法与数学的关系(笔记)</title>
<link>https://alili.tech/archive/97enyq3a3m/</link>
<pubDate>Tue, 15 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/97enyq3a3m/</guid>
<description>看到很多人说,数据结构,算法不能算作数学.
不同的数据结构,都是在编程中运用数学思维的产物。 每种数据结构都有自身的特点,有利于我们更方便地实现某种特定的数学模型.
数据结构 别小看这些数据结构,它们其实就是一个个解决问题的“模型”
数组 (Array) 特点: 可以通过下标,直接定位到所需的数据,适合随机访问.常常和循环语句相结合,来实现迭代法,例如二分查找、斐波那契数列等等
缺点: 数组只对稠密的数列更有效。如果数列非常稀疏,那么很多数组的元素就是无效值,浪费了存储空间。此外,数组中元素的插入和删除也比较麻烦,需要进行数据的批量移动。
如何解决稀疏数列问题: 链表
链表 (Linked List) 链表中的结点存储了数据,而链表结点之间的相连关系,在 JavaScript 语言中是通过对象引用来实现的。
特点: 不能通过下标来直接访问数据,而是必须按照存储的结构逐个读取
优势: 不必事先规定数据的数量,也不再需要保存无效的值,表示稀疏的数列时可以更有效的利用存储空间,同时也利于数据的动态插入和删除
缺点: 于数组而言,链表无法支持快速地随机访问,进行读写操作时就更耗时
哈希表 (Hash) 哈希表就可以通过数组和链表来构造,之前我们通过余数来实现哈希表.
优势:如果关键字已知则存取速度极快,插入块
缺点:删除慢,如果不知道关键则存取很慢,对存储空间使用不充分,会出现哈希冲突
树 (Tree) 优点:查找,插入,删除都快,树总是平衡的。类似的树对磁盘存储有用,不会出现哈希冲突
缺点:算法复杂
图(Graph) 图是另一种非线性数据结构。在图结构中,数据结点一般称为顶点,而边是顶点的有序偶对。如果两个顶点之间存在一条边,那么就表示这两个顶点具有相邻关系
栈 ( Stack) 先进后出的。在我们进行函数递归的时候,函数调用和返回的顺序,也是先进后出,所以,栈体现了递归的思想,可以实现基于递归的编程
队列 (Queue) 先进先出的数据结构,先进入队列的元素会优先得到处理.
在消息队列中,实现了生产者和消费者的松耦合,对消费者起到了保护作用,使它不容易被数据洪流冲垮。
编程语句 布尔表达式 体现了逻辑代数中逻辑和集合的概念
if(表达式) {函数体1} else {函数体2} // 若表达式为真,执行函数体1,否则执行函数体2。 循环语句 循环语句是迭代法(Newton's method)的体现
函数的调用 可以调用自己,也可以调用其他不同的函数。如果不断地调用自己,这就体现了递归的思想。
SQL 语言中的 Join 操作 Join 有多种类型,每种类型其实都对应了一种集合的操作。</description>
</item>
<item>
<title>数学篇 - 朴素贝叶斯(Naive Bayes)分类算法(笔记)</title>
<link>https://alili.tech/archive/6iwpimvelxh/</link>
<pubDate>Mon, 14 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/6iwpimvelxh/</guid>
<description>朴素贝叶斯(Naive Bayes) “用客观的新信息更新我们最初关于某个事物的信念后,我们就会得到一个新的、改进了的信念。” &mdash;- 数学家托马斯·贝叶斯(Thomas Bayes,1702~1761)
当你不能准确知悉一个事物的本质时,你可以依靠与事物特定本质相关的事件出现的多少去判断其本质属性的概率。
支持某项属性的事件发生得愈多,则该属性成立的可能性就愈大。
1774年,法国数学家皮埃尔-西蒙·拉普拉斯(Pierre-Simon Laplace,1749-1827)独立地再次发现了贝叶斯公式。
换种写法:
让计算机分辨水果 我们需要将水果的特征转化为计算机所能理解的数据。最常用的方式就是提取现实世界中的对象之属性,并将这些转化为数字。
比如:形状、外皮颜色、斑马纹理、重量、握感、口感。
将这些形容转化成数字,把重量由连续值转化成了离散值,这是因为朴素贝叶斯处理的都是离散值
扩大样本,仅仅 3 个水果还不足以构成朴素贝叶斯分类所需的训练样本
我们如何使用贝叶斯公式 用先验概率和条件概率估计后验概率。
假定数据对象的不同属性对其归类影响时是相互独立的。此时若数据对象 o 中同时出现属性 fi 与 fj,则对象 o 属于类别 c 的概率就是这样
朴素贝叶斯算法是假设各个特征之间相互独立,才可以两边相等,这也是朴素贝叶斯分类有朴素一词的来源
用 10 个水果的数据,来建立朴素贝叶斯模型
平滑(Smoothing) 会出现结果为 0 的情况,因此我们通常取一个比这个数据集里最小统计概率还要小的极小值,来代替“零概率”。比如,我们这里取 0.01。在填充训练数据中从来没有出现过的属性值的时候,我们就会使用这种技巧,我们给这种技巧起个名字就叫作平滑(Smoothing)。
例题: 假设我们有一个新的水果,它的形状是圆形,口感是甜的,那么根据朴素贝叶斯,它属于苹果、甜橙和西瓜的概率分别是多少呢?
apple 表示分类为苹果,shape-2 表示形状属性的值为 2(也就是圆形),taste-2 表示口感属性的值为 2。以此类推,我们还可计算该水果属于甜橙和西瓜的概率。
比较这三个数值,0.00198&lt;0.00798&lt;0.26934,所以计算机可以得出的结论,该水果属于甜橙的可能性是最大的
朴素贝叶斯分类主要包括这几个步骤 准备数据:针对水果分类这个案例,我们收集了若干水果的实例,并从水果的常见属性入手,将其转化为计算机所能理解的数据。这种数据也被称为训练样本。
建立模型:通过手头上水果的实例,我们让计算机统计每种水果、属性出现的先验概率,以及在某个水果分类下某种属性出现的条件概率。这个过程也被称为基于样本的训练。
分类新数据:对于一颗新水果的属性数据,计算机根据已经建立的模型进行推导计算,得到该水果属于每个分类的概率,实现了分类的目的。这个过程也被称为预测。
朴素贝叶斯分类的优缺点 优点:
算法逻辑简单,易于实现
分类过程中时空开销小</description>
</item>
<item>
<title>数学篇 - 概率之联合概率、条件概率、边缘概率和贝叶斯法则(笔记)</title>
<link>https://alili.tech/archive/haz1cu03hf/</link>
<pubDate>Sun, 13 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/haz1cu03hf/</guid>
<description>公式符号解释: P(A|B).是B已知下的A的条件概率
联合概率(Joint Probability) 各种解释描述,便于理解:
联合概率指的是包含多个条件且所有条件同时成立的概率,记作P(X=a,Y=b)或P(a,b),有的书上也习惯记作P(ab),但是这种记法个人不太习惯,所以下文采用以逗号分隔的记法。一定要注意是所有条件同时成立!
两个以上事件的交集的概率
例子:从一副扑克牌中抽出一张红色的4的概率为P(红4) = 2&frasl;52 = 1/26。(一副扑克牌有52张牌,想抽到的是红心4和方块4)。
条件概率(Conditional Probability) 条件概率是已知某(些)事件已经发生的前提下,另一(些)事件发生的概率。已知事件B已经发生时,事件A发生的条件概率写作P(A|B)。
条件概率表示在条件Y=b成立的情况下,X=a的概率,记作P(X=a|Y=b)或P(a|b),
例子:已知我们抽到了一张红色的牌,这张牌是4的概率为P(4|红) = 2&frasl;26 = 1&frasl;13 (一副扑克牌有52张牌,26张红色的,26张黑色的。现在因为我们已经抽到了一张红色的牌,我们知道我们抽取的范围是26张牌,因此第一个除数是26)。
联合概率与条件概率的区别 解释1: P(AB) 联合概率:池子没变。
P(A|B) 条件概率:池子变小了!!!!!!
所以如果用条件概率来算联合概率的话:池子不能小,给我乘回去!!!!
P(A|B) x P(B) = P(AB)
解释2: 当我们想要知道抽到一张红色的4的扑克牌的概率(红色和4的联合概率)时,我想让你想象一下,把所有52张牌面朝下放置,然后随机选中一张。在这52张牌中,有2张是红色的,同时数字是4(红心4和方块4)。所以联合概率是2/52 = 1/26。
而当我们想要知道已知抽中的牌是红色的时候,抽中数字是4的牌的概率,即条件概率P(4|红)时,我想让你再想象一下有52张牌。不过,在随机抽取一张牌之前,你给所有扑克牌排了个序,选中了所有26张红色的牌。现在你把这26张牌面朝下放置,然后随机选择一张牌。同样,这些红色的牌中有两张数字为4,所以条件概率是2/26 = 1&frasl;13
公式描述 边缘概率 解释:
对于离散型随机变量,我们可以通过通过联合概率 P(x, y) 在 y 上求和,就可以得到 P(x)。对于连续型随机变量,我们可以通过联合概率 P(x, y) 在 y 上的积分(无限求和),推导出概率 P(x)。这个时候,我们称 P(x) 为边缘概率。</description>
</item>
<item>
<title>数学篇 - 概率之随机变量与分布(笔记)</title>
<link>https://alili.tech/archive/6mchh1x7mrv/</link>
<pubDate>Sat, 12 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/6mchh1x7mrv/</guid>
<description>随机变量( random variable ) 设随机试验的样本空间为S SS,X = X ( e ) X=X(e)X=X(e)是定义在样本空间S SS上的实值单值函数。称X = X ( e ) X=X(e)X=X(e)为随机变量。 本质是关于基本事件的函数,自变量是基本事件,因变量是函数值。 随机试验: 满足:
(1)可重复性:试验在相同条件下可重复进行;
(2)可知性:每次试验的可能结果不止一个,并且事先能明确试验所有可能的结果;
(3)不确定性:进行一次试验之前不能确定哪一个结果会出现,但必然会出现结果中的一个。
样本空间: 随机试验的所有基本结果组成的集合称为样本空间。样本空间的元素称为样本点或基本事件。即样本空间本质是一个集合,每一个元素都是一次随机试验的结果。
样本和随机变量: 数理统计里的样本具有二重性,即样本既可以看作是一组观测值又可以看作是随机变量。
第一,在抽样之前。无法确定样本的观测值,所以可以看成是随机变量。
第二,样本在抽取以后,经观测,样本抽有了具体的观测值,故又可以看成是一组确定的值。
概率分布 我们拿最简单的抛硬币事件来看。从理论上说来,出现正面和反面的概率都是 50%
使用代码来尝试 function flipCoin(){ for (let index = 0; index &lt; 10; index++) { // 对随机数四舍五入 let randomNum = Math.round(Math.random()) // 随机为1则为正面 if(randomNum === 1){ console.</description>
</item>
<item>
<title>数学篇 - 树的深度优先搜索与广度优先搜索(笔记)</title>
<link>https://alili.tech/archive/5g1oligl91/</link>
<pubDate>Fri, 11 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/5g1oligl91/</guid>
<description>如何使用递归和栈实现深度优先搜索? 深度优先搜索的过程和递归调用在逻辑上是一致的。
写一个 TreeNode类代码,支持插入节点 class TreeNode { constructor(key){ this.key = key; this.sons = [] } insert(key){ let node = new TreeNode(key); this.sons.push(node) return node } } } 尝试创建一棵树 let str = 'hello word' let str2 = 'abcdefg' // 根节点 let root = new TreeNode(&quot;root&quot;) createTree(str,root) createTree(str2,root) function createTree(strs,parent){ if(strs.length !==0){ let found = parent.sons.find((item)=&gt;item.key === strs[0]) if(found){ let newStrs = strs.slice(1) createTree(newStrs,parent) }else{ let node = parent.insert(strs[0]) let newStrs = strs.</description>
</item>
<item>
<title>数学篇 - 树的概念(笔记)</title>
<link>https://alili.tech/archive/rakfaq9whbo/</link>
<pubDate>Thu, 10 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/rakfaq9whbo/</guid>
<description> 树的基本概念 树是由结点或顶点和边组成的(可能是非线性的)且不存在着任何环的一种数据结构。没有结点的树称为空(null或empty)树。一棵非空的树包括一个根结点,还(很可能)有多个附加结点,所有结点构成一个多级分层结构。
树是一种特殊的图 (后续文章会提到图).
前缀树 prefix tree (字典树 - trie) 有向树 它的边是有方向的。而树是没有简单回路的连通图。
以结点 v 为出发点的边的数量,我们叫作 v 的出度。而以 v为 终点的边之数量,称为 v 的入度。在上图中,结点 v2 的入度是 1,出度是 2。
回路和连通 高度与结点 二叉树 二叉树又分为:完美二叉树,完全二叉树,完满二叉树
完美二叉树(满二叉树) 除了叶子节点之外的每一个节点都有两个子节点,每一层(包括最后一层)都被完全填充 完全二叉树 完全二叉树从根结点到倒数第二层满足完美二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐 完满二叉树 所有非叶子结点的度都是2 换句话说:只要你有孩子,你就必然是有两个孩子。 </description>
</item>
<item>
<title>数学篇 - 动态规划,编辑距离的计算(笔记)</title>
<link>https://alili.tech/archive/nfo3tlig7y/</link>
<pubDate>Wed, 09 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/nfo3tlig7y/</guid>
<description>动态规划 (Dynamic Programming) 很多人也简称DP,动态规划需要通过子问题的最优解,推导出最终问题的最优解,因此这种方法特别注重子问题之间的转移关系。我们通常把这些子问题之间的转移称为状态转移,并把用于刻画这些状态转移的表达式称为状态转移方程。
编辑距离 (莱文斯坦距离,又称Levenshtein距离) 俄罗斯科学家弗拉基米尔·莱文斯坦(毕业于莫斯科国立大学数学和力学系)在1965年提出,他因对纠错码理论和信息理论的贡献,于2006年获得IEEE Richard W. Hamming奖章。
定义:莱文斯坦距离也称编辑距离,指的是将文本 A 编辑成文本 B 需要的最少变动次数(每次只能增加、删除或修改一个字)。
用途:可以用来计算字符串的相似度,文本相似度, 拼写纠错和抄袭侦测等等
优点:准确率很高,编辑距离算出来很小,文本相似度肯定很高。
缺点:召回率不高,由于编辑距离与文本的顺序有关。在文字相同,文字顺序变化很大的情况下,相似度会变得很低。比如“正大光明”和“光明正大”其实是一个意思。但编辑距离是4,完全不匹配
计算解析: 首先定义的单字符编辑操作有且仅有三种:
插入(Insertion)
删除(Deletion)
替换(Substitution)
搜索推荐关键词的应用 计算 mouuse 与 mouse的编辑距离
表格推导 这里面求最小值的 min 函数里有三个参数,分别对应三种情况的编辑距离,分别是:替换、插入和删除字符。
代码实现 /** * @Description: 使用状态转移方程,计算两个字符串之间的编辑距离 * @param a-第一个字符串,b-第二个字符串 * @return let-两者之间的编辑距离 */ function getStrDistance( a, b) { if (a == null || b == null) return -1; // 初始用于记录化状态转移的二维表 let d = [] for (let index = 0; index &lt;= a.</description>
</item>
<item>
<title>数学篇 - 组合,解决赛程规划与自然语言处理(笔记)</title>
<link>https://alili.tech/archive/yraotmb3ot/</link>
<pubDate>Tue, 08 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/yraotmb3ot/</guid>
<description>组合 组合(combination)是一个数学名词。一般地,从n个不同的元素中,任取m(m≤n)个元素为一组,叫作从n个不同元素中取出m个元素的一个组合。我们把有关求组合的个数的问题叫作组合问题。
对于所有 m 取值的组合之全集合,我们可以叫作全组合(All Combination)。例如对于集合{1, 2, 3}而言,全组合就是{空集, {1}, {2}, {3}, {1, 2}, {1,3} {2, 3}, {1, 2, 3}}。
如何安排世界杯赛程 想让全部的 32 支球队都和其他球队进行一次主客场的比赛.
自己不可能和自己比赛,不可重复的排列中,主场球队有 32 种选择,而客场球队有 31 种选择。那么一共要进行多少场比赛呢?很简单,就是 32x31=992 场!
在实际情况中,赛程设计并没有这样做.
这就是为什么要将所有 32 支队伍分成 8 个小组先进行小组赛的原因。一旦分成小组,每个小组的赛事就是 (4x3)/2=6 场。所有小组赛就是 6x8=48 场。
加上在 16 强阶段开始采取淘汰制,两两淘汰,所以需要 8+4+2+2=16 场淘汰赛(最后一次加 2 是因为还有 3、4 名的决赛),那么整个世界杯决赛阶段就是 48+16=64 场比赛。
这两两配对比赛的场次,我是如何计算出来的?让我引出今天的概念,组合(Combination)。
让计算机来组合队伍 /* * @Description: 使用函数的递归(嵌套)调用,找出所有可能的队伍组合 * @param teams-目前还剩多少队伍没有参与组合,result-保存当前已经组合的队伍 * @return void */ let teams = [&quot;t1&quot;, &quot;t2&quot;, &quot;t3&quot;]; combine(teams,[],2) function combine(teams,result,m) { // 挑选完了m个元素,输出结果 if (result.</description>
</item>
<item>
<title>数学篇 - 排列,解决田忌赛马与密码爆破问题(笔记)</title>
<link>https://alili.tech/archive/9kfvaensryf/</link>
<pubDate>Mon, 07 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/9kfvaensryf/</guid>
<description>田忌赛马的故事 田忌是齐国有名的将领,他常常和齐王赛马,可是总是败下阵来,心中非常不悦。孙膑想帮田忌一把。他把这些马分为上、中、下三等。他让田忌用自己的下等马来应战齐王的上等马,用上等马应战齐王的中等马,用中等马应战齐王的下等马。三场比赛结束后,田忌只输了第一场,赢了后面两场,最终赢得与齐王的整场比赛。
排列概念 从 n 个不同的元素中取出 m(1≤m≤n)个不同的元素,按照一定的顺序排成一列,这个过程就叫排列(Permutation)。
当 m=n 这种特殊情况出现的时候,这就是全排列(All Permutation)
田忌赛马的排列图 最终排列的数量 排列总数的计算 3x2x1=6
对于 n 个元素的全排列,所有可能的排列数量就是 nx(n-1)x(n-2)x…x2x1,也就是 n!
对于 n 个元素里取出m (0&lt;m≤n) 个元素的不重复排列数量是 nx(n-1)x(n-2)x…x(n - m + 1),也就是 n!/(n-m)!
使用代码计算田忌赛马的各种情况 // 齐王的马与速度 let q_horses_time = { q1: 1, q2: 2, q3: 3 } // 田忌的马与速度 let t_horses_time = { t1: 1.5, t2: 2.5, t3: 3.5 } // 他们马的名字 let q_horses = [&quot;q1&quot;, &quot;q2&quot;, &quot;q3&quot;] let t_horses = [&quot;t1&quot;, &quot;t2&quot;, &quot;t3&quot;] /** * @Description: 使用函数的递归(嵌套)调用,找出所有可能的马匹出战顺序 * @param horses-目前还剩多少马没有出战,result-保存当前已经出战的马匹及顺序 * @return void */ function permutate(horses, result) { // 所有马匹都已经出战,判断哪方获胜,输出结果 if (horses.</description>
</item>
<item>
<title>数学篇 - 递归,分而治之,从归并排序到MapReduce(笔记)</title>
<link>https://alili.tech/archive/zr4ve5abfzg/</link>
<pubDate>Sun, 06 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/zr4ve5abfzg/</guid>
<description>黄申老师的标题实在是太好了,找不到更好的标题来描述今天学习的内容.啊哈哈~
归并排序中的分治思想 问题: 对一堆杂乱无序的数字,按照从小到大或者从大到小的规则进行排序
有序情况 尝试合并有序数组{1, 2, 5, 8}和{3, 4, 6}的过程。 乱序情况 尝试把问题不断简化,也就是把数列不断简化,一直简化到只剩 1 个数。1 个数本身就是有序的,
把将长度为 n 的数列,每次简化为长度为 n-1 的数列,直至长度为 1。不过,这样的处理没有并行性,要进行 n-1 次的归并操作,但是效率会很低.
引入分而治之(Divide and Conquer)的思想 分而治之,我们通常简称为分治。它的思想就是,将一个复杂的问题,分解成两个甚至多个规模相同或类似的子问题,然后对这些子问题再进一步细分,直到最后的子问题变得很简单,很容易就能被求解出来,这样这个复杂的问题就求解出来了。
一个数组的排序 两个数组排序后合并
最重要的思想在于如何拆解问题 归并排序的不同阶段
使用递归的方式来实现已上思路 // 递归拆分数组 function merge_sort(to_sort) { // 非法数据,直接返回[] if (!to_sort) return []; // 如果分解到只剩一个数,返回该数 if (to_sort.length == 1) return to_sort; // 将数组分解成左右两半 let mid = to_sort.length / 2; // js中的splice会操作原数组内容, // 前半段取出来之后,后半段直接取原数组的变量应用就好了 let left = [].</description>
</item>
<item>
<title>数学篇 - 递归,复杂问题分解(笔记)</title>
<link>https://alili.tech/archive/ru7lce72gge/</link>
<pubDate>Sat, 05 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/ru7lce72gge/</guid>
<description>递归与循环 理论上所有递归能做到的循环都能实现.
递归和循环其实都是迭代法的实现,而且在某些场合下,它们的实现是可以相互转化的。
为什么要使用递归 既然递归的函数值返回过程和基于循环的迭代法一致,我们直接用迭代法不就好了,为什么还要用递归的数学思想和编程方法呢?
如何在限定总和的情况下,求所有可能的加和方式? 假设有四种面额的钱币,1 元、2 元、5 元和 10 元,要奖励别人10元,那可以奖赏 1 张 10 元,或者 10 张 1 元,或者 5 张 1 元外加 1 张 5 元等等。最终会有多少种方案?
如何把复杂的问题简单化? // 面额 var rewards = [1, 2, 5, 10]; /** * @Description: 使用函数的递归(嵌套)调用,找出所有可能的奖赏组合 * @param totalReward-奖赏总金额,result-保存当前的解 * @return void */ function get(totalReward,result){ // 如果所有奖励全部给完 if (totalReward == 0) { // 拿到复合条件的结果,输出 console.log(result); return; } // 如果奖励的钱超过当初设想的奖励(钱给多了), // 则不是我们想要的结果 if (totalReward &lt; 0) { return; } //根据不同面额触发,让他们开始递归 for (let i = 0; i &lt; rewards.</description>
</item>
<item>
<title>数学篇 - 数学归纳法,给计算机注入灵魂(笔记)</title>
<link>https://alili.tech/archive/fexppeuk3m/</link>
<pubDate>Fri, 04 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/fexppeuk3m/</guid>
<description>什么是数学归纳法? 在棋盘上放麦粒的规则是,第一格放一粒,第二格放两粒,以此类推,每一小格内都比前一小格多一倍的麦子,直至放满 64 个格子。你发现第 1 格到第 8 格的麦子数分别是:1、2、4、8、16、32、64、128。
找规律 对于类似这种无穷数列的问题,我们通常可以采用数学归纳法(Mathematical Induction)来证明
数学归纳法步骤 证明基本情况(通常是 n=1 的时候)是否成立; 假设 n=k−1 成立,再证明 n=k 也是成立的(k 为任意大于 1 的自然数)。 和使用迭代法的计算相比,数学归纳法最大的特点就在于“归纳”二字。它已经总结出了规律。只要我们能够证明这个规律是正确的,就没有必要进行逐步的推算,可以节省很多时间和资源。
代码示例 let grid = 63; console.time('归纳法耗时') console.log(`舍罕王给了这么多粒: ${ Math.pow(2, grid) - 1 }`) console.timeEnd('归纳法耗时') 递归调用的代码和数学归纳法的逻辑是一致的,但是数学归纳法实现的运行时间几乎为 0
数学归纳法需要我们能做出合理的命题假设,然后才能进行证明。</description>
</item>
<item>
<title>数学篇 - 迭代法,让每次计算都更接近真像(笔记)</title>
<link>https://alili.tech/archive/35dkyj5swxr/</link>
<pubDate>Thu, 03 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/35dkyj5swxr/</guid>
<description>什么是迭代法(Iterative Method)? 就是不断地用旧的变量值,递推计算新的变量值。
小故事: 古印度国王舍罕酷爱下棋,他打算重赏国际象棋的发明人宰相西萨·班·达依尔。这位聪明的大臣指着象棋盘对国王说:“陛下,我不要别的赏赐,请您在这张棋盘的第一个小格内放入一粒麦子,在第二个小格内放入两粒,第三小格内放入给四粒,以此类推,每一小格内都比前一小格加一倍的麦子,直至放满 64 个格子,然后将棋盘上所有的麦粒都赏给您的仆人我吧!”国王自以为小事一桩,痛快地答应了。可是,当开始放麦粒之后,国王发现,还没放到第二十格,一袋麦子已经空了。随着,一袋又一袋的麦子被放入棋盘的格子里,国王很快看出来,即便拿来全印度的粮食,也兑现不了对达依尔的诺言。
通过一个函数来计算最后麦子的数量. 用计算机语言其实特别适合 function getNumberOfWheat(grid){ numberOfWheatInGrid = 0; // 当前格子里麦粒的数量 let numberOfWheatInGrid = 1; // 第一个格子里麦粒的数量 // 先放一粒米 sum += numberOfWheatInGrid; for (let i = 2; i &lt;= grid; i ++) { numberOfWheatInGrid *= 2; // 当前格子里麦粒的数量是前一格的2倍 sum += numberOfWheatInGrid; // 累计麦粒总数 } return sum; } // 计算64格的数量 console.log(getNumberOfWheat(64)) 具体应用? 求数值的精确或者近似解。典型的方法包括二分法(Bisection method)和牛顿迭代法(Newton’s method)。
在一定范围内查找目标值。典型的方法包括二分查找。
机器学习算法中的迭代。相关的算法或者模型有很多,比如 K- 均值算法(K-means clustering)、PageRank 的马尔科夫链(Markov chain)、梯度下降法(Gradient descent)等等。迭代法之所以在机器学习中有广泛的应用,是因为很多时候机器学习的过程,就是根据已知的数据和一定的假设,求一个局部最优解。而迭代法可以帮助学习算法逐步搜索,直至发现这种解。</description>
</item>
<item>
<title>数学篇 - 余数与哈希函数(笔记)</title>
<link>https://alili.tech/archive/jvh7xaof84/</link>
<pubDate>Wed, 02 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/jvh7xaof84/</guid>
<description>余数 例1: 今天是星期三,你想知道 50 天之后是星期几,那你可以这样算,拿 50 除以 7(因为一个星期有 7 天),然后余 1,最后在今天的基础上加一天,这样你就能知道 50 天之后是星期四了
例2: 如果你要展示 1123 条数据,每页 10 条,那该怎么计算总共的页数呢?我想你肯定是拿 1123 除以 10,最后得到商是 112,余数是 3,所以你的总页数就是 112+1=113,而最后的余数就是多出来,凑不够一页的数据。
余数总是在一个固定的范围内
同余定理 数学上,两个整数除以同一个整数,若得相同余数,则二整数同余
如何理解 100 天里,所有星期一的这些天都是同余的,所有星期二的这些天就是同余的,同理,星期三、星期四等等这些天也都是同余的
哈希 Hash 将任意长度的输入,通过哈希算法,压缩为某一固定长度的输出
想想星期的概念: 将数据取余 ps. size指的是有限空间的数目而不是大小
在这个公式中,x 表示等待被转换的数值,而 size 表示有限存储空间的数目,mod 表示取余操作。通过余数,你就能将任何数值,转换为有限范围内的一个数值,然后根据这个新的数值,来确定将数据存放在何处。
假设有两条记录,它们的记录标号分别是 1 和 101。我们把这些模 100 之后余数都是 1 的,存放到第 1 个可用空间里。以此类推,将余数为 2 的 2、102、202 等,存放到第 2 个可用空间,将 100、200、300 等存放到第 100 个可用空间里。
再复杂一点 我们假设随机数 MAX 是 590199,那么我们针对标号为 1 的记录进行重新计算,最后的计算结果就是 0,而针对标号 101 的记录,如果随机数 MAX 取 627901,对应的结果应该是 2。这样先前被分配到空间 1 的两条记录,在新的计算公式作用下,就会被分配到不同的可用空间中。</description>
</item>
<item>
<title>数学篇 - 计算机的源头二进制(笔记)</title>
<link>https://alili.tech/archive/ja86xk20l2/</link>
<pubDate>Tue, 01 Sep 2020 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/ja86xk20l2/</guid>
<description>计算机起源 计算机的起源是数学中的二进制计数法 什么是二进制计数法? 日常的十进制 阿拉伯数字由从 0 到 9 这样 10 个计数符号组成,并采取进位制法,每10进一位,2871为例
其中 ^ 表示幂或次方运算。十进制的数位(千位、百位、十位等)全部都是 10^n 的形式。需要特别注意的是,任何非 0 数字的 0 次方均为 1。在这个新的表示式里,10 被称为十进制计数法的基数.
二进制 我们将基数改为2,就可以理解二进制的展示了.例如110101.
二进制的数位就是 2^n 的形式
其他进制 我们只要基于基数的改动,就可以使用任一进制来展示我们的数字.
JavaScript 进制转换 parseInt(num,8); //八进制转十进制 parseInt(num,16); //十六进制转十进制 parseInt(num).toString(8) //十进制转八进制 parseInt(num).toString(16) //十进制转十六进制 parseInt(num,2).toString(8) //二进制转八进制 parseInt(num,2).toString(16) //二进制转十六进制 parseInt(num,8).toString(2) //八进制转二进制 parseInt(num,8).toString(16) //八进制转十六进制 parseInt(num,16).toString(2) //十六进制转二进制 parseInt(num,16).toString(8) //十六进制转八进制 计算机为什么使用二进制? 组成计算机系统的逻辑电路通常只有两个状态,即开关的接通与断开。
二进制的位操作 向左移位 二进制 110101 向左移一位,就是在末尾添加一位 0,因此 110101 就变成了 1101010
如果将 1101010 换算为十进制,就是 106,你有没有发现,106 正好是 53 的 2 倍 &gt; 二进制左移一位,其实就是将数字翻倍。</description>
</item>
<item>
<title>Puppeteer在工作中是如何伪装自己的(爬虫与反爬虫)</title>
<link>https://alili.tech/archive/7fp151i7xnf/</link>
<pubDate>Sat, 29 Aug 2020 20:32:05 +0000</pubDate>
<guid>https://alili.tech/archive/7fp151i7xnf/</guid>
<description>为了更好保护我们的数据与程序安全.
今天就介绍一下,如何检测访问我们的web程序是否为无头浏览器, 以及他们的一些反检测的方法.
Webdriver检测 一般来说,如果是无头浏览器模式下, navigator.webdriver 会返回 true.
检测方式 最简单的检测方式
if (navigator.webdriver) { // 针对无头浏览器的操作 } 使用defineProperty删除webdriver后如何检测 如果对方使用以下方式删除了webdriver属性,其实还是有办法检测的
Object.defineProperty(navigator, 'webdriver', { get: () =&gt; undefined, }) 应对方案 以下方式,还是可以检测出来
webdriver in navigator navigator.hasOwnProperty(&quot;webdriver&quot;) 绕过方法 直接删掉webdriver属性,这是我目前验证成功的方法. 目前其他网上找到的方法已经无效.
await page.evaluateOnNewDocument(() =&gt; { const newProto = navigator.__proto__; delete newProto.webdriver; navigator.__proto__ = newProto; }); chrome属性检测 检测方式 在无头浏览器模式下,全局对象下的chrome对象是没有 runtime属性的
if (!window.chrome || !window.chrome.runtime) { // 无头浏览器模式... } 绕过方法 所以绕过方法也很简单,我们只需要伪造一个</description>
</item>
<item>
<title>Taro跨端开发之让Taro UI支持React Native</title>
<link>https://alili.tech/archive/wwfw2u086as/</link>
<pubDate>Thu, 27 Aug 2020 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/wwfw2u086as/</guid>
<description>Taro UI 不支持RN的窘境 Taro UI 文档上很早就说明会有可能支持rn了,但是快一年多了,因为taro ui团队人力的问题一直没有兼容到rn.
业务紧迫,我们等不到那一天了.自己动手丰衣足食.
Taro 传统组件打包在RN上的问题 一般来说,组件库打完包之后 dist/index.js文件会是这样的.
根据运行时的环境变量区分是否要引入哪一个组件库.
if (process.env.TARO_ENV === 'weapp') { module.exports = require('./weapp/ui') module.exports.default = module.exports } else if (process.env.TARO_ENV === 'h5') { module.exports = require('./h5/ui') module.exports.default = module.exports } else { module.exports = require('./weapp/ui') module.exports.default = module.exports } 理想模式下,只要加入一个rn的环境判断,就可以做到rn组件库的引入了.
if (process.env.TARO_ENV === 'weapp') { module.exports = require('./weapp/ui') module.exports.default = module.exports } else if (process.env.TARO_ENV === 'h5') { module.exports = require('.</description>
</item>
<item>
<title>Taro跨端开发之多业务模块管理 React Native篇(终篇)</title>
<link>https://alili.tech/archive/2f9lla2yjb5/</link>
<pubDate>Tue, 25 Aug 2020 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/2f9lla2yjb5/</guid>
<description>React Native 热更新方案 rn的业务越来越庞大,同时协同的团队越来越多. rn的动态化就必须提上日程了. 对于rn热更新,首当其冲的问题就是分包.
rn的基础库很大,再加上我们依赖了很多的三方库.这些代码就必须在分包的时候单独剥离出来. 业务包让他比较纯粹的只有业务代码. 这样就可以保证业务包的体积比较小,保证热更新时候的速度.
使用metro分包 React Native 提供的 metro 自带分包功能。metro我们本来就一直在用,只要在metro打包的时候,提供相应的打包规则. 就可以实现rn的分包了.
示例: ios打包
node ./node_modules/react-native/local-cli/cli.js bundle --platform ios --dev false --entry-file rn入口文件.js --bundle-output ./xxx/ --assets-dest ./xxx/ --config /{你的绝对路径}/你的metro配置文件.js metro 关键api介绍 我们分包需要用的选项主要是两个:
createModuleIdFactory:这个函数传入要打包的 module 文件的绝对路径,返回这个 module 在打包的时候生成的 id。
processModuleFilter:这个函数传入 module 信息,返回一个 boolean 值,false 则表示这个文件不打入当前的包。
主工程分包 之前我们有提到过我们有一个项目是主工程,里面没有任何的业务代码.只有一些代码运行需要的所有依赖.
我们需要将所有的依赖全部收集起来,当业务模块打包的时候,发现本地有这个依赖就可以使用 processModuleFilter方法排除掉.
因为我们的主工程与业务项目的依赖版本都是高度统一的. 所以我们node_modules下面的依赖包路径都是完全一致的.
Taro跨端开发之依赖管理
主工程的metro配置文件示例:
function createModuleIdFactory() { return path =&gt; { // 在这里我们拿到依赖的文件路径, // 我们需要在这个函数块中,将路径以收集并且将这些数据生成文件 // 部署到我们内网的服务器中 // 当业务模块需要打包的时候,是否要将代码打进包中,将以这个文件为依据 return path; }; } module.</description>
</item>
<item>
<title>Taro跨端开发之多业务模块管理 React Native篇(中)</title>
<link>https://alili.tech/archive/j4ylxksvfa8/</link>
<pubDate>Sun, 23 Aug 2020 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/j4ylxksvfa8/</guid>
<description>实现一个简单的模块管理工具,解决npm拉取业务模块慢的问题 使用npm拉取git仓库,速度其实还是可以接受的. 主要慢的地方在于 postinstall的时候需要在npm拉取git仓库的时候,需要重新构建项目代码.还有一点就是业务模块也有依赖,npm install的时候也需要重新拉取相同的依赖.
这两个步骤其实是可以省略的,解决的办法就是可以实现一个简单的模块管理工具来替代npm加载业务模块的处理.
创建一个配置文件 module.exports = { &quot;Module1&quot;: { &quot;url&quot;: &quot;http://xxx1.git&quot;, &quot;moduleName&quot;: [ &quot;模块名称&quot; ], &quot;branch&quot;: &quot;develop&quot;, &quot;type&quot;: &quot;taro&quot; }, &quot;Module2&quot;: { &quot;url&quot;: &quot;http://xxx2.git&quot;, &quot;moduleName&quot;: [ &quot;模块名称&quot; ], &quot;branch&quot;: &quot;develop&quot;, &quot;type&quot;: &quot;taro&quot; } } 因为每一个项目都会用到这一个文件,把这个文件放到服务器上. 在业务模块初始化的时候,自动拉取解析这个文件.再使用nodejs拉取相应的git仓库.
拉取git仓库 使用download-git-repo拉取git仓库的代码放到node_modules目录下. 这样在项目中,我就可以引用普通npm一样引入这个仓库的代码了.
这是download-git-repo的地址与使用方法 https://www.npmjs.com/package/download-git-repo
模拟npm生命周期 代码拉下来之后,还需要用postinstall来构建业务代码, 所以需要使用nodejs遍历以下业务模块的目录,读取目录下的每一个packages.json.
如果有npm钩子的,就执行一下里面的script.
大致示例代码如下:
// 示例代码,仅供参考 const spawn = require('cross-spawn'); const fse = require('fs-extra'); const packageData = fse.readJsonSync('./packages.json'); if (packageData.scripts.preinstall) { spawn.sync('npm',['run','preinstall']); } if (packageData.</description>
</item>
<item>
<title>Taro跨端开发之多业务模块管理 React Native篇(上)</title>
<link>https://alili.tech/archive/w650d9ok34i/</link>
<pubDate>Fri, 21 Aug 2020 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/w650d9ok34i/</guid>
<description>原生RN项目与Taro如何共存? 我们团队一开始就有实践原生React Native的项目,很长一段时间,
所有的业务模块都是在一个项目里面开发维护,时间久了这个项目就变成了一个巨无霸项目.
再加上后来引进了基于Taro开发的rn项目,为了保证原生与Taro的RN共存,
不管是原生rn项目还是taro项目的package.json文件的main对外导出的索引文件格式都是一致的.
我们使用以下方案来维护我们的代码.
使用npm来管理我们的业务模块 RN业务的主工程 这是我最初实践的方案,首先我们创建一个项目为RN的主工程.
里面没有任何的业务代码,只有在根目录下有一个index.js的业务索引文件.
它大概是这样的:
import { AppRegistry} from 'react-native'; import Module1 from 'Module1'; import Module2 from 'Module2'; import Module3 from 'Module3'; AppRegistry.registerComponent('Module1', () =&gt; Module1); AppRegistry.registerComponent('Module2', () =&gt; Module2); AppRegistry.registerComponent('Module3', () =&gt; Module3); 之前的一篇文章我已经提到,我们所有的模块依赖都是统一的,并且版本锁死.
有兴趣的可以点击查看:
Taro跨端开发之依赖管理
所以我的主工程的依赖与业务模块的依赖是保持一致的.
使用NPM管理业务模块 我们会把所有的业务模块当成npm来管理,因为npm有很多的生命周期钩子使用.
在统一了npm script 命令之后,很容易统一管理他们.
当然这些业务模块我们不会把他发布到npm服务器上,因为业务代码会频繁变动,如果每一次提交都要上传到npm服务器,自然会添加开发人员的代码管理成本(发布npm包很烦的)
所以我们使用 npm + git 地址来拉取我们的业务模块.
例如:
主工程的package.json { &quot;name&quot;: &quot;base&quot;, &quot;version&quot;: &quot;0.0.6&quot;, &quot;scripts&quot;: { &quot;build&quot;:&quot;构建rn的相关操作&quot; }, &quot;dependencies&quot;: { &quot;公共依赖包&quot;: &quot;1.</description>
</item>
<item>
<title>Taro跨端开发之依赖管理问题</title>
<link>https://alili.tech/archive/h8gasmt9u5c/</link>
<pubDate>Thu, 20 Aug 2020 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/h8gasmt9u5c/</guid>
<description>目前我们通过Taro触达的端已经有:
微信小程序 QQ小程序 百度小程序 React Native H5 快应用 为了快速的让业务可以触达到各端,我们每一个业务模块都是独立的.
他们根据业务需要,分别兼容到不同的端,等到构建的时候再将其进行组装成为一个完整的项目.
为了满足以上需要,并且可以灵活开发.所以每一个业务模块都是可以独立开发,独立运行的,可以不依赖主工程.
目前来说项目之间的关系已经比较类似微前端了.
因为大部分端(除了React Native) 都不能做到热更新,所以相对微前端架构而言小程序们做不到独立部署.
关于Taro + React Native部分,后面会有专门的文章跟大家介绍我们是怎么玩的.
今天就跟大家介绍一下,多业务,多团队,多端的代码管理中我们遇到的第一个问题: 依赖管理问题
所有项目依赖统一管理 因为业务模块很多,我们项目工程的差异必须保持高度统一,其中包括依赖.
核心依赖集中管理 现在我们团队开发的跨端模块已经有几十个.为了方便管理,
我将所有的依赖封装成了一个npm包,里面没有任何的业务代码,只有稳定的依赖.
所有的项目都会引入这个核心的依赖,以保证所有项目可以稳定运行.
非核心依赖版本管理问题 如果是非核心依赖,我们将使用我们自研的cli工具强行修改package.json里面的版本号.
持续集成的过程中,所有的跨端项目都会通过这个cli工具在npm install执行之前修改我们的项目配置.其中就包括了我们的依赖.
我们修改过cli工具之后,所有的跨端项目都会更新到最新的依赖配置.
个别项目特殊处理办法 所有项目的依赖全部统一之后,总会有一些特殊原因个别项目的依赖会有一点区别.
这里就必须要提到package.json的 resolutions属性.
resolutions 字段用于解析选择性版本,可以通过此功能自定义依赖版本。
这样npm就会将多版本共存的版本,强行指定某一版本,满足个别项目的特定需求.
如果我要所有的 B 强行指定2.0
使用方法如下:
// package.json // 这样B这个依赖库,就被强行指定版本了 { &quot;resolutions&quot;: { &quot;B&quot;: &quot;2.0.0&quot; } } resolutions的解释,你可以在这里查看更多:
https://classic.yarnpkg.com/zh-Hans/docs/selective-version-resolutions
当然你也可以使用这个工具,将依赖强制指定版本:
https://www.npmjs.com/package/npm-force-resolutions
第三方依赖稳定性问题 我们在开发周期比较长的前端项目的时候,必然会遇到依赖管理的问题.
我们在开发项目的时候,我们用了大量的三方库.这些三方的依赖库时不时的会更新自己的代码.
第三方依赖库的代码更新会很容易造成代码运行的不稳定,
比如昨天还跑的好好的项目,另一位刚刚接手的同学重新安装依赖之后项目就完全跑不起来了.
或者自己机器跑的好好的代码,扔到打包机上重新打包之后就完全跑不起来.</description>
</item>
<item>
<title>Taro跨端开发之跨端开发新时代的思考与举措</title>
<link>https://alili.tech/archive/bjg2zsidz0a/</link>
<pubDate>Tue, 16 Jun 2020 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/bjg2zsidz0a/</guid>
<description>新时代 跨端框架的出现,前端的浏览器兼容过渡到了客户端平台兼容的新时代.
对于初创公司而言,这种框架可以快速试错与降低人力与时间成本.
为什么选择Taro 并不是说Taro方案有多优秀,就当前时间节点看来目前的所有跨端框架都还处于完善阶段.最完善的端可能就是小程序与H5了.
但是对于有着对跨端开发有着强烈需求的公司来说,这些还远远不够.
只是第一眼看上去Taro比较适合我们的现有业务.后续各种端的数量增大,如何从容对待.
目前也还是未知数.就团队而言,前端输出突然猛增,其他配套设施没跟上等于随时翻车.目前我们面临的最大问题就是测试资源的问题.
对于跨端技术而言,目前相对完善且符合公司业务的技术框架可以选择
Taro
uni-app
一个类React, 一个类Vue.
在框架层面,两种技术框架提供着非常接近的多端兼容方案.
最大的不同是React 与 Vue的区别.当然,uni-app 提供了小程序容器方案.
taro这边目前还没有.
就目前对Taro来看,直接实现Taro to uni小程序理论上是可以实现的.
在Taro next版本中,已经支持对vue语法, 那这样后续是否会出现多端框架to多端框架的局面呢?
回到问题本身,为什么我们选择Taro呢,
团队组建之初使用React技术栈
我们在RN端有了一定的探索
Taro支持快应用
就历史包袱来看,选择Taro过渡成本是相对较小的.
目标 一处代码,多端运行.
减少维护成本
多端快速上线
如果就快速上线来看,人力成本在短期内看,是非常可观的.但是一旦项目变大,团队变得也越大的时候.
自动化测试,而项目工程化没有跟上,那就直接天堂变地狱.
处境与心态 目前使用taro开发单端,基本上没有太大的问题.目前遇到的问题,都有办法可以解决的.
唯一占用开发工作量的是,如何处理代码在多端中的兼容问题.
所以处理兼容问题的方法与技巧还有多端开发的意识就尤为重要了.
还有一个就是开发者的心态问题,因为要开发多端,多端差异还是会有的.
在开发多端的同时,也要学习多端的开发方式.一来二去的,很容易形态奔溃.
但是如果跨越了这一时期,了解了各端的特点,后续面对一些问题都会游刃有余了~
适配最佳路径 经过各种实践,我们认为一下开发的兼容路径是相对简单的.
在适配的过程中,主要还是样式的问题比较多.如果反向适配,会极其痛苦,
因为你之前写的任意一行代码,都可能是后面的bug.
可能在后续的发展中,这种类似短板问题会越来越不明显.</description>
</item>
<item>
<title>Switch XCI转NSP工具 - 4NXCI下载</title>
<link>https://alili.tech/archive/zzabj3gn1as/</link>
<pubDate>Sun, 02 Feb 2020 19:02:36 +0000</pubDate>
<guid>https://alili.tech/archive/zzabj3gn1as/</guid>
<description> 4NXCI 是将 XCI 文件转换为 NSP 的工具 目前只支持windows,其他系统不支持
Github地址: https://github.com/The-4n/4NXCI
下载地址 https://github.com/The-4n/4NXCI/releases
使用方法 GUI工具 命令用不习惯的可以下载GUI的版本,文件命名大致为4nxci-v4.03_GUI.zip
命令行工具 .\4nxci.exe [options...] &lt;path_to_file.xci&gt; Options: -k, --keyset 设置keyset文件路径, 默认为 ./keys.dat -h, --help 显示帮助信息 -t, --tempdir 设置临时文件夹 -o, --outdir 设置导出文件夹 -r, --rename 设置导出的nsp文件的文件名 --keepncaid 保持当前ncas ID 关于keyset 文件 拷贝以下代码放到一个txt文件中,然后重命名为 keys.dat 使用GUI或者命令行工具导入就可以了
master_key_00 = C2CAAFF089B9AED55694876055271C7D master_key_01 = 54E1B8E999C2FD16CD07B66109ACAAA6 master_key_02 = 4F6B10D33072AF2F250562BFF06B6DA3 master_key_03 = 84E04EC20B9373818C540829CF147F3D master_key_04 = CFA2176790A53FF74974BFF2AF180921 master_key_05 = C1DBEDCEBF0DD6956079E506CFA1AF6E master_key_06 = 0AA90E6330CDC12D819B3254D11A4E1E header_key = AEAAB1CA08ADF9BEF12991F369E3C567D6881E4E4A6A47A51F6E4877062D542D aes_kek_generation_source = 4D870986C45D20722FBA1053DA92E8A9 aes_key_generation_source = 89615EE05C31B6805FE58F3DA24F7AA8 key_area_key_application_source = 7F59971E629F36A13098066F2144C30D key_area_key_ocean_source = 327D36085AD1758DAB4E6FBAA555D882 key_area_key_system_source = 8745F1BBA6BE79647D048BA67B5FDA4A titlekek_source = 1EDC7B3B60E6B4D878B81715985E629B aes_kek_generation_source = 4d870986c45d20722fba1053da92e8a9 aes_key_generation_source = 89615ee05c31b6805fe58f3da24f7aa8 bis_kek_source = 34c1a0c48258f8b4fa9e5e6adafc7e4f eticket_rsa_kek = 19c8b441d318802bad63a5beda283a84 eticket_rsa_kek_source = dba451124ca0a9836814f5ed95e3125b eticket_rsa_kekek_source = 466e57b74a447f02f321cde58f2f5535 header_kek_source = 1f12913a4acbf00d4cde3af6d523882a header_key = aeaab1ca08adf9bef12991f369e3c567d6881e4e4a6a47a51f6e4877062d542d header_key_source = 5a3ed84fdec0d82631f7e25d197bf5d01c9b7bfaf628183d71f64d73f150b9d2 key_area_key_application_00 = ef979e289a132c23d39c4ec5a0bba969 key_area_key_application_01 = cdedbab97b69729073dfb2440bff2c13 key_area_key_application_02 = 75716ed3b524a01dfe21456ce26c7270 key_area_key_application_03 = f428306544cf5707c25eaa8bc0583fd1 key_area_key_application_04 = 798844ec099eb6a04b26c7c728a35a4d key_area_key_application_05 = a57c6eecc5410ada22712eb3ccbf45f1 key_area_key_application_06 = 2a60f6c4275df1770651d5891b8e73ec key_area_key_application_07 = 32221bd6ed19b938bec06b9d36ed9e51 key_area_key_application_08 = fb20aa9e3dbf67350e86479eb431a0b3 key_area_key_application_09 = ce8d5fa79e220d5f48470e9f21be018b key_area_key_application_source = 7f59971e629f36a13098066f2144c30d key_area_key_ocean_00 = b33813e4c9c4399c75fabc673ab4947b key_area_key_ocean_01 = c54166efa8c9c0f6511fa8b580191677 key_area_key_ocean_02 = 3061ce73461e0b0409d6a33da85843c8 key_area_key_ocean_03 = 06f170025a64921c849df168e74d37f2 key_area_key_ocean_04 = dc857fd6dc1c6213076ec7b902ec5bb6 key_area_key_ocean_05 = 131d76b70bd8a60036d8218c15cb610f key_area_key_ocean_06 = 17d565492ba819b0c19bed1b4297b659 key_area_key_ocean_07 = 37255186f7678324bf2b2d773ea2c412 key_area_key_ocean_08 = 4115c119b7bd8522ad63c831b6c816a6 key_area_key_ocean_09 = 792bfc652870cca7491d1685384be147 key_area_key_ocean_source = 327d36085ad1758dab4e6fbaa555d882 key_area_key_system_00 = 6dd02aa15b440d6231236b6677de86bc key_area_key_system_01 = 4ab155e7f29a292037fd147592770b12 key_area_key_system_02 = b7a74adeaf89c2a198c327bdff322d7d key_area_key_system_03 = d5aab1acd23a8aec284a316df859d377 key_area_key_system_04 = 9b44b45b37de9d14754b1d22c2ca742c key_area_key_system_05 = 0012e957530d3dc7af34fbbe6fd44559 key_area_key_system_06 = 01744e3b0818445cd54ee9f89da43192 key_area_key_system_07 = d0d30e46f5695b875f11522c375c5a80 key_area_key_system_08 = bd06cb1b86bd5c433667470a09eb63de key_area_key_system_09 = e19f788f658eda8bbf34a1dd2a9503a9 key_area_key_system_source = 8745f1bba6be79647d048ba67b5fda4a keyblob_00 = f759024f8199101dddc1ef91e6eecf37e24b95ac9272f7ae441d5d8060c843a48322d21cdd06d4fc958c68d3800eb4db939ffbec930177f77d136144ff615aa8835e811bb958deda218f8486b5a10f531b30cb9d269645ac9fc25c53fc80525e56bd3602988a9fcf06bbf99ca910ad6530791d512c9d57e17abf49220de6419bf4eca1685c1e4df77f19db7b44a985ca keyblob_01 = bd27264ae07e979756411d0c66e679e3c50851f3e902d9c2cd1a438b948159a517ec1566c10570326ea2697ee62da46f14bb5d581bfc06fd0c9387ea33d2d4dc63e7809ba90f03dd2c7112ffbfa548951b9b8c688b5e4f2951d24a73da29c668154a5d4838dba71ee068ace83fe720e8c2a495c596f73525dc3c05994b40ad27f8c60322f75cd548b821af9162e16f76 keyblob_02 = a3d4a8e153b8e6ae6e6aef3e8f219cb4b7790f47856accc76268f9afa99a1ff8b1a72f63d1f99f480a3c1532078bb59abdd25203cfb12a38b33e9ba6a09afb6f24283b3ba76a0161230a73669ddf5493c2b7919d094fd795b484794854f71e4f4c672245d7770e29397722444d111b4229cdbf35707b70634ea8f140766e884cc580cb1e2d9aa9866ffef920010fc409 keyblob_03 = 1558f525ae8c5be9243fb6d8a8b0a8ee0e886a59035668740a936619b7a5c83e821198b171d18e51445054df68688e45703b936818a827d8e540dd6bef2e11ec9ddc6cfe5fc736dd769b9f6e0a23a62e2e5f49e86143646a04ec3a23f828373a336a5c224a91f8a0c6c6a7b5844dd6415804209f83c943aeca9cfd856db6bd4ec32009c8cb268ed053052c9237dfd8bc keyblob_04 = 9fbeb1957fc1629e08b753a9086d6e01ffb4f11466b7417e3fa7f5f1efb754406704fd75afaf91a408a0b524c1fc80d36c2046fa4757412efe4c11e382f72e8a10d90ed580017d9deb87af2549b6b02661af48ff94f6072c0fef7fc2833b8bdae503898e2e927ac0663e8b6391dd4f1d685313935e2c48ece7d177c88bc9c883ede36c3677495784b838d7265c6ba7a1 keyblob_05 = 94a92da1d73c2b3e165c891ced5607fc6628ca2a0654f3fbc05711c063377c6e9c96a9d0192e530dd510e4fd41aa62ef4213c5f6e059e7e21db098a9b22d1e6c29bee148aaef15c52549d9165de96e85b0d029ecdc5843e2f32cb18be707eec61909cf3385d45bc2a4c8d76e9bfad5a40c4b92dcb982aa50d474897ac9ebb5351a7015dcc277a08f1214ad41384d7941 keyblob_key_00 = 4f5a86ec7c47b6b8663e0bbc199dcafa keyblob_key_01 = 746c6b5e8f62b3f418ade81ddcde7619 keyblob_key_02 = 1b24583f656650fc214bf2b59d8de0fd keyblob_key_03 = 71a7924e6c03baac7f2b99fac10c6201 keyblob_key_04 = 8f91661e8fc6ff81930ddea915ced49d keyblob_key_05 = e1e960a7edf905dcd3b125d8377f7600 keyblob_key_source_00 = df206f594454efdc7074483b0ded9fd3 keyblob_key_source_01 = 0c25615d684ceb421c2379ea822512ac keyblob_key_source_02 = 337685ee884aae0ac28afd7d63c0433b keyblob_key_source_03 = 2d1f4880edeced3e3cf248b5657df7be keyblob_key_source_04 = bb5a01f988aff5fc6cff079e133c3980 keyblob_key_source_05 = d8cce1266a353fcc20f32d3b517de9c0 keyblob_mac_key_00 = c98dd8afff06306bc72495e8c89eed4a keyblob_mac_key_01 = 240d461913b37f6bc74027001581542c keyblob_mac_key_02 = f4915b454f3a03661955cf4579d59876 keyblob_mac_key_03 = 323a27241392a8cad2b3c87086b211d2 keyblob_mac_key_04 = 5620355b2d504e4af5bd5cde9875b035 keyblob_mac_key_05 = c08712b1e1ff64f9e66fb6298ba27d6b keyblob_mac_key_source = 59c7fb6fbe9bbe87656b15c0537336a5 master_kek_00 = f759024f8199101dddc1ef91e6eecf37 master_kek_01 = bd27264ae07e979756411d0c66e679e3 master_kek_02 = a3d4a8e153b8e6ae6e6aef3e8f219cb4 master_kek_03 = 1558f525ae8c5be9243fb6d8a8b0a8ee master_kek_04 = 9fbeb1957fc1629e08b753a9086d6e01 master_kek_05 = 94a92da1d73c2b3e165c891ced5607fc master_kek_source_06 = 374b772959b4043081f6e58c6d36179a master_kek_source_07 = 9a3ea9abfd56461c9bf6487f5cfa095c master_kek_source_08 = dedce339308816f8ae97adec642d4141 master_kek_source_09 = 1aec11822b32387a2bedba01477e3b67 master_key_00 = c2caaff089b9aed55694876055271c7d master_key_01 = 54e1b8e999c2fd16cd07b66109acaaa6 master_key_02 = 4f6b10d33072af2f250562bff06b6da3 master_key_03 = 84e04ec20b9373818c540829cf147f3d master_key_04 = cfa2176790a53ff74974bff2af180921 master_key_05 = c1dbedcebf0dd6956079e506cfa1af6e master_key_06 = 0aa90e6330cdc12d819b3254d11a4e1e master_key_07 = 929f86fbfe4ef7732892bf3462511b0e master_key_08 = 23cfb792c3cb50cd715da0f84880c877 master_key_09 = 75c93b716255319b8e03e14c19dea64e master_key_10 = 73767484C73088F629B0EEB605F64AA6 master_key_source = d8a2410ac6c59001c61d6a267c513f3c package1_key_00 = f4eca1685c1e4df77f19db7b44a985ca package1_key_01 = f8c60322f75cd548b821af9162e16f76 package1_key_02 = c580cb1e2d9aa9866ffef920010fc409 package1_key_03 = c32009c8cb268ed053052c9237dfd8bc package1_key_04 = ede36c3677495784b838d7265c6ba7a1 package1_key_05 = 1a7015dcc277a08f1214ad41384d7941 package2_key_00 = a35a19cb14404b2f4460d343d178638d package2_key_01 = a0dd1eacd438610c85a191f02c1db8a8 package2_key_02 = 7e5ba2aafd57d47a85fd4a57f2076679 package2_key_03 = bf03e9889fa18f0d7a55e8e9f684323d package2_key_04 = 09df6e361e28eb9c96c9fa0bfc897179 package2_key_05 = 444b1a4f9035178b9b1fe262462acb8e package2_key_06 = 442cd9c21cfb8914587dc12e8e7ed608 package2_key_07 = 70c821e7d6716feb124acbac09f7b863 package2_key_08 = 8accebcc3d15a328a48365503f8369b6 package2_key_09 = f562a7c6c42e3d4d3d13ffd504d77346 package2_key_source = fb8b6a9c7900c849efd24d854d30a0c7 per_console_key_source = 4f025f0eb66d110edc327d4186c2f478 retail_specific_aes_key_source = e2d6b87a119cb880e822888a46fba195 rsa_oaep_kek_generation_source = a8ca938434127fda82cc1aa5e807b112 rsa_private_kek_generation_source = ef2cb61a56729b9157c38b9316784ddd save_mac_kek_source = d89c236ec9124e43c82b038743f9cf1b save_mac_key = 536b210e185bccae8e36c622891bf7f6 save_mac_key_source = e4cd3d4ad50f742845a487e5a063ea1f sd_card_kek_source = 88358d9c629ba1a00147dbe0621b5432 sd_card_nca_key_source = 5841a284935b56278b8e1fc518e99f2b67c793f0f24fded075495dca006d99c2 sd_card_save_key_source = 2449b722726703a81965e6e3ea582fdd9a951517b16e8f7f1f68263152ea296a sd_seed = fd324d2dcf64f196f78b83f66786f71d secure_boot_key = 9add3555ee6479259837e9e4eb42286d ssl_rsa_kek = b011100660d1dccbad1b1b733afa9f95 ssl_rsa_kek_source_x = 7f5bb0847b25aa67fac84be23d7b6903 ssl_rsa_kek_source_y = 9a383bf431d0bd8132534ba964397de3 titlekek_00 = 62a24d6e6d0d0e0abf3554d259be3dc9 titlekek_01 = 8821f642176969b1a18021d2665c0111 titlekek_02 = 5d15b9b95a5739a0ac9b20f600283962 titlekek_03 = 1b3f63bcb67d4b06da5badc7d89acce1 titlekek_04 = e45c1789a69c7afbbf1a1e61f2499459 titlekek_05 = ddc67f7189f4527a37b519cb051eee21 titlekek_06 = b1532b9d38ab036068f074c0d78706ac titlekek_07 = 81dc1b1783df268789a6a0edbf058343 titlekek_08 = 47dfe4bf0eeda88b17136b8005ab08ea titlekek_09 = adaa785d90e1a9c182ac07bc276bf600 titlekek_source = 1edc7b3b60e6b4d878b81715985e629b tsec_key = 06d4c3db4a0c6692ffab54b2c70b6935 aes_kek_generation_source = 4d870986c45d20722fba1053da92e8a9 aes_key_generation_source = 89615ee05c31b6805fe58f3da24f7aa8 bis_kek_source = 34c1a0c48258f8b4fa9e5e6adafc7e4f eticket_rsa_kek = 19c8b441d318802bad63a5beda283a84 eticket_rsa_kek_source = dba451124ca0a9836814f5ed95e3125b eticket_rsa_kekek_source = 466e57b74a447f02f321cde58f2f5535 header_kek_source = 1f12913a4acbf00d4cde3af6d523882a header_key = aeaab1ca08adf9bef12991f369e3c567d6881e4e4a6a47a51f6e4877062d542d header_key_source = 5a3ed84fdec0d82631f7e25d197bf5d01c9b7bfaf628183d71f64d73f150b9d2 key_area_key_application_00 = ef979e289a132c23d39c4ec5a0bba969 key_area_key_application_01 = cdedbab97b69729073dfb2440bff2c13 key_area_key_application_02 = 75716ed3b524a01dfe21456ce26c7270 key_area_key_application_03 = f428306544cf5707c25eaa8bc0583fd1 key_area_key_application_04 = 798844ec099eb6a04b26c7c728a35a4d key_area_key_application_05 = a57c6eecc5410ada22712eb3ccbf45f1 key_area_key_application_06 = 2a60f6c4275df1770651d5891b8e73ec key_area_key_application_07 = 32221bd6ed19b938bec06b9d36ed9e51 key_area_key_application_08 = fb20aa9e3dbf67350e86479eb431a0b3 key_area_key_application_09 = ce8d5fa79e220d5f48470e9f21be018b key_area_key_application_source = 7f59971e629f36a13098066f2144c30d key_area_key_ocean_00 = b33813e4c9c4399c75fabc673ab4947b key_area_key_ocean_01 = c54166efa8c9c0f6511fa8b580191677 key_area_key_ocean_02 = 3061ce73461e0b0409d6a33da85843c8 key_area_key_ocean_03 = 06f170025a64921c849df168e74d37f2 key_area_key_ocean_04 = dc857fd6dc1c6213076ec7b902ec5bb6 key_area_key_ocean_05 = 131d76b70bd8a60036d8218c15cb610f key_area_key_ocean_06 = 17d565492ba819b0c19bed1b4297b659 key_area_key_ocean_07 = 37255186f7678324bf2b2d773ea2c412 key_area_key_ocean_08 = 4115c119b7bd8522ad63c831b6c816a6 key_area_key_ocean_09 = 792bfc652870cca7491d1685384be147 key_area_key_ocean_source = 327d36085ad1758dab4e6fbaa555d882 key_area_key_system_00 = 6dd02aa15b440d6231236b6677de86bc key_area_key_system_01 = 4ab155e7f29a292037fd147592770b12 key_area_key_system_02 = b7a74adeaf89c2a198c327bdff322d7d key_area_key_system_03 = d5aab1acd23a8aec284a316df859d377 key_area_key_system_04 = 9b44b45b37de9d14754b1d22c2ca742c key_area_key_system_05 = 0012e957530d3dc7af34fbbe6fd44559 key_area_key_system_06 = 01744e3b0818445cd54ee9f89da43192 key_area_key_system_07 = d0d30e46f5695b875f11522c375c5a80 key_area_key_system_08 = bd06cb1b86bd5c433667470a09eb63de key_area_key_system_09 = e19f788f658eda8bbf34a1dd2a9503a9 key_area_key_system_source = 8745f1bba6be79647d048ba67b5fda4a keyblob_00 = f759024f8199101dddc1ef91e6eecf37e24b95ac9272f7ae441d5d8060c843a48322d21cdd06d4fc958c68d3800eb4db939ffbec930177f77d136144ff615aa8835e811bb958deda218f8486b5a10f531b30cb9d269645ac9fc25c53fc80525e56bd3602988a9fcf06bbf99ca910ad6530791d512c9d57e17abf49220de6419bf4eca1685c1e4df77f19db7b44a985ca keyblob_01 = bd27264ae07e979756411d0c66e679e3c50851f3e902d9c2cd1a438b948159a517ec1566c10570326ea2697ee62da46f14bb5d581bfc06fd0c9387ea33d2d4dc63e7809ba90f03dd2c7112ffbfa548951b9b8c688b5e4f2951d24a73da29c668154a5d4838dba71ee068ace83fe720e8c2a495c596f73525dc3c05994b40ad27f8c60322f75cd548b821af9162e16f76 keyblob_02 = a3d4a8e153b8e6ae6e6aef3e8f219cb4b7790f47856accc76268f9afa99a1ff8b1a72f63d1f99f480a3c1532078bb59abdd25203cfb12a38b33e9ba6a09afb6f24283b3ba76a0161230a73669ddf5493c2b7919d094fd795b484794854f71e4f4c672245d7770e29397722444d111b4229cdbf35707b70634ea8f140766e884cc580cb1e2d9aa9866ffef920010fc409 keyblob_03 = 1558f525ae8c5be9243fb6d8a8b0a8ee0e886a59035668740a936619b7a5c83e821198b171d18e51445054df68688e45703b936818a827d8e540dd6bef2e11ec9ddc6cfe5fc736dd769b9f6e0a23a62e2e5f49e86143646a04ec3a23f828373a336a5c224a91f8a0c6c6a7b5844dd6415804209f83c943aeca9cfd856db6bd4ec32009c8cb268ed053052c9237dfd8bc keyblob_04 = 9fbeb1957fc1629e08b753a9086d6e01ffb4f11466b7417e3fa7f5f1efb754406704fd75afaf91a408a0b524c1fc80d36c2046fa4757412efe4c11e382f72e8a10d90ed580017d9deb87af2549b6b02661af48ff94f6072c0fef7fc2833b8bdae503898e2e927ac0663e8b6391dd4f1d685313935e2c48ece7d177c88bc9c883ede36c3677495784b838d7265c6ba7a1 keyblob_05 = 94a92da1d73c2b3e165c891ced5607fc6628ca2a0654f3fbc05711c063377c6e9c96a9d0192e530dd510e4fd41aa62ef4213c5f6e059e7e21db098a9b22d1e6c29bee148aaef15c52549d9165de96e85b0d029ecdc5843e2f32cb18be707eec61909cf3385d45bc2a4c8d76e9bfad5a40c4b92dcb982aa50d474897ac9ebb5351a7015dcc277a08f1214ad41384d7941 keyblob_key_00 = 4f5a86ec7c47b6b8663e0bbc199dcafa keyblob_key_01 = 746c6b5e8f62b3f418ade81ddcde7619 keyblob_key_02 = 1b24583f656650fc214bf2b59d8de0fd keyblob_key_03 = 71a7924e6c03baac7f2b99fac10c6201 keyblob_key_04 = 8f91661e8fc6ff81930ddea915ced49d keyblob_key_05 = e1e960a7edf905dcd3b125d8377f7600 keyblob_key_source_00 = df206f594454efdc7074483b0ded9fd3 keyblob_key_source_01 = 0c25615d684ceb421c2379ea822512ac keyblob_key_source_02 = 337685ee884aae0ac28afd7d63c0433b keyblob_key_source_03 = 2d1f4880edeced3e3cf248b5657df7be keyblob_key_source_04 = bb5a01f988aff5fc6cff079e133c3980 keyblob_key_source_05 = d8cce1266a353fcc20f32d3b517de9c0 keyblob_mac_key_00 = c98dd8afff06306bc72495e8c89eed4a keyblob_mac_key_01 = 240d461913b37f6bc74027001581542c keyblob_mac_key_02 = f4915b454f3a03661955cf4579d59876 keyblob_mac_key_03 = 323a27241392a8cad2b3c87086b211d2 keyblob_mac_key_04 = 5620355b2d504e4af5bd5cde9875b035 keyblob_mac_key_05 = c08712b1e1ff64f9e66fb6298ba27d6b keyblob_mac_key_source = 59c7fb6fbe9bbe87656b15c0537336a5 master_kek_00 = f759024f8199101dddc1ef91e6eecf37 master_kek_01 = bd27264ae07e979756411d0c66e679e3 master_kek_02 = a3d4a8e153b8e6ae6e6aef3e8f219cb4 master_kek_03 = 1558f525ae8c5be9243fb6d8a8b0a8ee master_kek_04 = 9fbeb1957fc1629e08b753a9086d6e01 master_kek_05 = 94a92da1d73c2b3e165c891ced5607fc master_kek_source_06 = 374b772959b4043081f6e58c6d36179a master_kek_source_07 = 9a3ea9abfd56461c9bf6487f5cfa095c master_kek_source_08 = dedce339308816f8ae97adec642d4141 master_kek_source_09 = 1aec11822b32387a2bedba01477e3b67 master_key_00 = c2caaff089b9aed55694876055271c7d master_key_01 = 54e1b8e999c2fd16cd07b66109acaaa6 master_key_02 = 4f6b10d33072af2f250562bff06b6da3 master_key_03 = 84e04ec20b9373818c540829cf147f3d master_key_04 = cfa2176790a53ff74974bff2af180921 master_key_05 = c1dbedcebf0dd6956079e506cfa1af6e master_key_06 = 0aa90e6330cdc12d819b3254d11a4e1e master_key_07 = 929f86fbfe4ef7732892bf3462511b0e master_key_08 = 23cfb792c3cb50cd715da0f84880c877 master_key_09 = 75c93b716255319b8e03e14c19dea64e master_key_0a = 73767484C73088F629B0EEB605F64AA6 master_key_source = d8a2410ac6c59001c61d6a267c513f3c package1_key_00 = f4eca1685c1e4df77f19db7b44a985ca package1_key_01 = f8c60322f75cd548b821af9162e16f76 package1_key_02 = c580cb1e2d9aa9866ffef920010fc409 package1_key_03 = c32009c8cb268ed053052c9237dfd8bc package1_key_04 = ede36c3677495784b838d7265c6ba7a1 package1_key_05 = 1a7015dcc277a08f1214ad41384d7941 package2_key_00 = a35a19cb14404b2f4460d343d178638d package2_key_01 = a0dd1eacd438610c85a191f02c1db8a8 package2_key_02 = 7e5ba2aafd57d47a85fd4a57f2076679 package2_key_03 = bf03e9889fa18f0d7a55e8e9f684323d package2_key_04 = 09df6e361e28eb9c96c9fa0bfc897179 package2_key_05 = 444b1a4f9035178b9b1fe262462acb8e package2_key_06 = 442cd9c21cfb8914587dc12e8e7ed608 package2_key_07 = 70c821e7d6716feb124acbac09f7b863 package2_key_08 = 8accebcc3d15a328a48365503f8369b6 package2_key_09 = f562a7c6c42e3d4d3d13ffd504d77346 package2_key_source = fb8b6a9c7900c849efd24d854d30a0c7 per_console_key_source = 4f025f0eb66d110edc327d4186c2f478 retail_specific_aes_key_source = e2d6b87a119cb880e822888a46fba195 rsa_oaep_kek_generation_source = a8ca938434127fda82cc1aa5e807b112 rsa_private_kek_generation_source = ef2cb61a56729b9157c38b9316784ddd save_mac_kek_source = d89c236ec9124e43c82b038743f9cf1b save_mac_key = 536b210e185bccae8e36c622891bf7f6 save_mac_key_source = e4cd3d4ad50f742845a487e5a063ea1f sd_card_kek_source = 88358d9c629ba1a00147dbe0621b5432 sd_card_nca_key_source = 5841a284935b56278b8e1fc518e99f2b67c793f0f24fded075495dca006d99c2 sd_card_save_key_source = 2449b722726703a81965e6e3ea582fdd9a951517b16e8f7f1f68263152ea296a sd_seed = fd324d2dcf64f196f78b83f66786f71d secure_boot_key = 9add3555ee6479259837e9e4eb42286d ssl_rsa_kek = b011100660d1dccbad1b1b733afa9f95 ssl_rsa_kek_source_x = 7f5bb0847b25aa67fac84be23d7b6903 ssl_rsa_kek_source_y = 9a383bf431d0bd8132534ba964397de3 titlekek_00 = 62a24d6e6d0d0e0abf3554d259be3dc9 titlekek_01 = 8821f642176969b1a18021d2665c0111 titlekek_02 = 5d15b9b95a5739a0ac9b20f600283962 titlekek_03 = 1b3f63bcb67d4b06da5badc7d89acce1 titlekek_04 = e45c1789a69c7afbbf1a1e61f2499459 titlekek_05 = ddc67f7189f4527a37b519cb051eee21 titlekek_06 = b1532b9d38ab036068f074c0d78706ac titlekek_07 = 81dc1b1783df268789a6a0edbf058343 titlekek_08 = 47dfe4bf0eeda88b17136b8005ab08ea titlekek_09 = adaa785d90e1a9c182ac07bc276bf600 titlekek_source = 1edc7b3b60e6b4d878b81715985e629b tsec_key = 06d4c3db4a0c6692ffab54b2c70b6935 xci_header_key = 01C58FE7002D135AB29A3F69339574B1 </description>
</item>
<item>
<title>2019 年终有感</title>
<link>https://alili.tech/archive/awarkaubgq7/</link>
<pubDate>Sun, 29 Dec 2019 21:29:00 +0000</pubDate>
<guid>https://alili.tech/archive/awarkaubgq7/</guid>
<description>今年可谓是充实的一年,进了新公司.一切的都变了,职场非常顺利.也很感谢自己的同事这一年里对我的帮助与启发.
相对2018,是一个飞跃性的成长.越想越觉得应该早点来到这样的公司,充分展示自己.
在新公司里把之前所有关于前端自动化的设想全部在公司全面落地. 微前端架构也在公司的后台管理系统上得到了真正的应用. 并且在基于Taro多端同构的应用上取得了一定的成绩.
我的团队 在公司做了几件漂亮事之后,我重新又开始带团队,这一次跟以往完全不一样. 大家磨合的很好,彻彻底底的感受到了合作的魅力. 最终大家的成绩都得到了公司的认可,都拿到了满意的绩效.
接下来的三个月,我会推动可支持多端的组件库建设,一款真正可以支持 H5,微信小程序,RN的组件库. 大家对此都表示非常的积极,要是做成了,我觉得这是一个非常了不起的事情. 希望今年的第一季度可以有一个很好的结果.
关于博客 今年可能更新博客不是特别频繁,最主要的原因还是忙(懒). 本来想好好介绍一下我们的自动化方案,还有一些多端同构方面的探索与沉淀的. 最终还是没有写出来.2020年的话,还是要把这些沉淀与思考输出出来最好.
关于技术 今年的技术,都是对去年设想真正的落地,包括微前端,自动化,多端同构方面. 但是最近我一直问自己,如何做到1个人干十个人的活. 一直问一直问,就总会有很多想法冒出来,不管是低代码也好,D2C也好都是非常好的提效工具.
近期团队开始对Taro的D2C开始探索,初步算了一下.编写UI代码的最大可以提升12倍. 用D2C的方式去构建多端应用的话,很多的样式问题可以机械化的解决,这样大家说不定都可以直接忽略样式在多端之间的差异. 并且基于设计稿,可以一分钟写一个页面.这对团队来说是一个质的飞跃. 这是一个非常好的方向.
关于2020 该干的还是得继续干,有的同事还是在痴迷自动化方面得事情.
我感觉就现在得团队来说,工程自动化再花很多心思去做的话,对于现在的收益是在递减的.
因为现有的自动化架构已经完全满足现在的开发需要了.我想我们更多的是在其他地方下功夫.来做到团队效率的整体提升.
2020年上半年,会跟设计部门合作一个对外网站,主要分享公司的技术与设计的沉淀.到时候就请大家拭目以待吧.</description>
</item>
<item>
<title>持续集成 - 代码质量扫描</title>
<link>https://alili.tech/archive/t0uky1fwdy/</link>
<pubDate>Sun, 15 Dec 2019 23:15:00 +0000</pubDate>
<guid>https://alili.tech/archive/t0uky1fwdy/</guid>
<description>为了方便管理公司的代码质量,让代码质量扫描跟持续集成结合到一起是重要的一步. 目前公司比较年轻,但是在短短的一年时间里,也有了接近300个前端项目.
这么多的前端项目,我们如何保证代码质量就成了一个很重要的事情.
代码项目太多,codereview是必要的.但是人肉codereview不能保证效率. 因为个人水平的差距,与公司规范的熟悉程度.不能保证所有项目的规范一致性. 还有一些潜在的bug也有可能会被漏掉.
我们公司是基于Gitlab CI/CD,所以以下说明是基于Gitlab的方式.但是思路的运用是相通的,有需要的人可以借鉴思路.
修改.gitlab-ci.yml 为了方便每一个项目接入,我们对配置做了很小的改动. 并且每一个项目的配置的修改都是一样的,就是为了方便无脑的复制粘贴.
我们还做了一个cli工具,里面包含代码扫描的一切功能.安装在runner的机器上.
image: node:11.10.0 stages: - codereview # 添加一个codereview的stage codereview: stage: codereview script: - cli codereview # cli工具触发codereview tags: - fe #runner的tag,根据自己的情况自行修改 cli 触发代码扫描做什么? 执行SonarQube扫描 关于SonarQube的安装,网上有很多教程请自行搜索.
在执行runner的机器上安装 sonar-scanner npm i sonar-scanner -g 利用自研的cli工具,在项目根目录生成sonar 扫描的配置文件. // 获取gitlab 注入ci的环境变量 const { CI_PROJECT_NAME, CI_PROJECT_ID, } = process.env; // 基于gitlab的项目id生成一个sonar的projectKey const projectBuffer = Buffer.from('sonar' + CI_PROJECT_ID); const projectKey = projectBuffer.</description>
</item>
<item>
<title>FREE TO SHOT - 马</title>
<link>https://alili.tech/archive/ja0kb23zw4o/</link>
<pubDate>Sat, 07 Dec 2019 21:32:05 +0000</pubDate>
<guid>https://alili.tech/archive/ja0kb23zw4o/</guid>
<description>摄于 2019年12月7日 乌鲁木齐</description>
</item>
<item>
<title>持续集成 - 使用Gilab CI进行前端项目的持续集成</title>
<link>https://alili.tech/archive/tisoqlkd0qa/</link>
<pubDate>Fri, 20 Sep 2019 23:15:00 +0000</pubDate>
<guid>https://alili.tech/archive/tisoqlkd0qa/</guid>
<description>市面上的持续集成平台有很多,今天介绍Gitlab的CI.
从Gitlab 8.0开始,Gitlab CI 就集成在了Gitlab中.
使用方法非常简单,只要我们在项目的根目录创建一个 .gitlab-ci.yml文件,添加一个Runner,就直接接入了Gitlab CI.
接入方式非常的简单便捷.目前我们在前端脚手架中放一个.gitlab-ci.yml文件,后续每一个前端项目都可以按照标准直接接入Gitlab CI.
GitLab Runner 所有的Gitlab任务都会放在Gitlab Runner中执行.
GitLab Runner的安装环境,根据你的需求而定,一个gitlab 可以注册的Runner是没有限制的.
普通的前端项目直接安装在Linux中就可以了,如果是小程序或者RN这种项目,目前我是直接找了一台mac mini来安装Runner.
GitLab Runner安装 安装Runner 非常简单, 这里晒出Gitlab的官方安装教程,你可以根据你的系统环境自行下载.
安装Gitlab Runner 官方文档
注册Runner Runner安装好之后,想要关联到你的Gitlab,需要注册Runner.
这里给大家介绍群组的runner注册方式,个人项目的runner方式注册基本一致
注册Gitlab Runner 官方文档
大致流程为:
打开Gitlab网站,选择群组-&gt; 设置-&gt; CI/CD -&gt; 展开Runner -&gt; 你会看到注册Runner的Token与Url 安装好Runner的机器上,运行 sudo gitlab-runner register 输入 你Gitlab的 URL 输入 Token 输入 Runner 的名字 选择 Runner 的类型,没有特殊需求直接选shell 完成 配置Runner Runner的配置文件会以执行的用户身份不同而不同
当GitLab Runner以root身份执行时 /etc/gitlab-runner/config.toml 当GitLab Runner以非root身份执行时 ~/.gitlab-runner/config.toml Runner的全局配置 这里我只说关键的两点</description>
</item>
<item>
<title>前端微服务化进阶4 - 跨框架共享组件(微件化)</title>
<link>https://alili.tech/archive/vgnhe9tfqnc/</link>
<pubDate>Sat, 22 Jun 2019 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/vgnhe9tfqnc/</guid>
<description>在微前端中,我们可以根据自己的业务需求,让子模块使用不同框架技术栈.虽然到了这一步已经很美好了,那这就是微前端的终点吗?
答案是否定的,微前端的边界还可以更进一步的拓宽.
上一篇微前端的文章 https://alili.tech/archive/qh7x5i5szfh/ 给大家介绍了,如何在相同技术栈的子模块之间,相互调用React组件.
那今天要说的就是,如何在不同技术栈之间的子模块相调用不同技术栈的组件.
最终,我们只需要根据我们的需求调用相关功能的组件,我们不需要管他是 react ,vue或者是angular写的.
你只管用,只知道他是一个组件就好了,不用关心太多~ 对于团队的组件积累,是有极大好处的.
场景 一般情况下,一个公司的前端团队的技术栈都是统一的.但也有前端团队使用不统一技术栈的时候. 比如:
时代的变迁,升级技术栈导致内部技术栈不统一 项目众多,因为需求不一致,其他的技术栈对于项目更加有力 &hellip;其他管理原因 当我们已经使用微前端架构来构建我们的项目的时候,我们的子模块有可能因为我们项目的需求导致使用了其他的技术栈,
如果我们使用了其他的技术栈,我们原来封装的组件就不能在新的项目中用了,所以我们需要要求组件可以跨框架共享使用.
我们该怎么做? 这里有提到微件仓库模块,这是一个单独的项目.你可以理解是以前的旧项目,当你需要这个旧项目的某一个组件的时候,可以直接从这个项目里面拿.
你也可以做成一个只提供组件的项目,毕竟在运行时一个子模块挂载到我们的项目中来是没有任何资源消耗的.
我们只要知道我们需要的组件从哪里来就行了,然后根据组件还有之前定义好的路由找到这个组件,调用他,使用他就好了.
基于Web component封装我们的组件 不同框架开发的组件,差异很大.想要串在一起使用,基本上是不可能的. 好在目前所有的框架都支持让组件以webcomponent的形式存在.
react: https://react.docschina.org/docs/web-components.html
vue : https://github.com/vuejs/vue-web-component-wrapper
angular: https://www.angular.cn/guide/elements#transforming-components-to-custom-elements
关于Web Components 的详细介绍 https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
加载性能 如果一个页面依赖了很多跨框架的组件,必然出现网络方面的性能问题.
我们会在请求的中间加一层node服务,当页面请求多个跨框架的组件的时候,我们的node就会合并成单个文件,并且保存在硬盘上.
所以说,当这个页面被请求过之后,页面零散的组件便会合并在一起,第二次其他用户请求就不会有这种合并文件的处理,直接返回静态资源给客户端.
这种方式也不会对nodejs有太多额外的压力,
因为现在的页面结构还是相对静态稳定的,没有太多的动态定制化的东西.这个方案足以应付大多数的应用场景.
尾巴 经过不停的探索,微前端终于走到了微件化的这一步,感慨颇多~
我们从一个窗口只能加载单个页面, 再到多个页面(SPA), 再到现在的多个项目(微前端), 然后再可以控制不同组件在多个项目之间随意组合(微件化).
微前端的应用边界应该还可以拓展的更宽,还可以开发出更多惊喜的操作.
相关系列文章 https://alili.tech/tags/microfrontend/</description>
</item>
<item>
<title>前端微服务化进阶3 - 跨模块共享组件</title>
<link>https://alili.tech/archive/qh7x5i5szfh/</link>
<pubDate>Sun, 12 May 2019 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/qh7x5i5szfh/</guid>
<description>前端微服务化之后,我们会面临一个问题: 模块之间重复代码不能复用的问题.
如果使用npm管理我们的重复代码,我们会多出维护npm包的成本. 在子模块更新npm包版本,也是一件很麻烦的事情. 在js文件体积上也没有任何的优化.
组件共享 今天我们就来聊一聊如何在多个模块中同时使用一个组件.
思路 在base模块管理公共组件,将组件封装成动态组件,这样在打包的时候我们就可以将该组件切割成单独文件了. 当其他的子模块需要这个组件的时候,向Base模块动态获取.
实践 动态组件的封装 为了让其他模块可以按需加载我们的公共组件,我们需要对已有的组件封装成动态组件.
我这里使用的是 umi/dynamic,
他是基于https://github.com/jamiebuilds/react-loadable 封装了一层. 有兴趣的小伙伴可以自行了解.
import React from 'react'; import dynamic from 'umi/dynamic'; import PageLoading from '@/components/PageLoading' export const Demo = dynamic(import( `../Demo`), {loading: () =&gt; &lt;PageLoading /&gt;}) export default Demo; 对外提供获取动态组件的方法 在加载Base模块的时候,我们可以在window下暴露一个调用该模块动态组件的方法
window.getDynamicComponent = async function(name) { let component = null; component = await import(`@/components/dynamic/${name}`); return component[name]; }; 子模块调用公共组件 因为base模块提供了一个获取公共组件的全局方法, 我们就可以在任何模块任何需要调用公共组件的地方去是使用它了.
// 获取组件 let component = await window.</description>
</item>
<item>
<title>前端微服务化进阶2 - 本地开发指南</title>
<link>https://alili.tech/archive/3xwbcv1w21i/</link>
<pubDate>Mon, 22 Apr 2019 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/3xwbcv1w21i/</guid>
<description>使用single-spa构建我们的微服务化的前端应用之后,其实有一个问题会一直困扰着我们, 就是如何有效的开发?如何与我们平时开发的前端应用一样简单,容易上手. 今天就以umi子模块为例,希望给到大家一个思路
今天我就介绍一种方法,希望对大家有帮助.
模块加载器 是否还记得我之前的模块加载器, https://alili.tech/archive/1a60cede/
我们只需要将原来模块的加载器,封装成npm包.
然后在我们开发子模块项目的时候,运行我们的加载器 // umi src/app.js import bootstrap from '@demo/demo-module-dev-loader' //封装过后的npm包 import store from 'store'; // 我们用于通讯的store文件 export async function render(oldRender) { if (process.env.NODE_ENV === 'development') { const main = oldRender(); const res = await window.fetch('./project.json'); let currentProject = await res.json(); bootstrap({ main, store, prefix: currentProject.prefix }); } else { oldRender(); } } module-dev-loader 我们的demo-module-dev-loader里一样会有一个 Bootstrap.js文件,我们对他进行一些小的修改.
import * as singleSpa from 'single-spa'; import { registerApp,registerLocal } from '.</description>
</item>
<item>
<title>前端微服务化进阶1 - 基于umi的子模块方案</title>
<link>https://alili.tech/archive/9xuojm75d2a/</link>
<pubDate>Sat, 13 Apr 2019 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/9xuojm75d2a/</guid>
<description>距离第一篇聊前端微服务的文章已经时隔大半年,很多人对此感兴趣.
今天我们就聊一聊,我们如何基于umi来打造一个更完善的前端微服务的子模块.
如果你用的是react以外的前端技术栈, 我的很多处理做法也可以应用在其他技术栈上.
希望对你也有所帮助.
优秀的umi框架 在前端中后台项目上,前端微服务化的需求相对是比较旺盛一些的.
说到中后台,很多企业都是基于antd的组件来构建自己的项目.
自去年的see conf之后,蚂蚁的一款可插拔的企业级 react 应用框架 umi发布了.
这款框架与antd息息相关,antd结合umi使用那是相当的自然与流畅.
可以说,基于umi与antd构建的项目非常的漂亮.这么优秀的框架,如果让他适用于我们的前端微服务架构,岂不美哉?
umi也有相关的类似微服务方案: https://github.com/umijs/umi-example-monorepo
但是umi提供的方案,有很大的局限性. 如果可以接入single-spa的微服务方案,独立开发,独立部署等等的前端微服务化红利, 会让你的项目日后有更大的发展空间.
基于umi插件机制做到前端微服务化 umi 提供了非常强大的插件机制,正是由于这一点,我们才可以让umi也可以接入到微服务架构中来
umi插件介绍 umi插件的基本介绍:
https://umijs.org/zh/plugin/
umi插件开发 这里介绍了如何开发一个简单的umi插件:
https://umijs.org/zh/plugin/develop.html
接入single-spa的umi插件 export default (api, opts) =&gt; { // 以下的所有代码都写在这里面哦 }; 渲染入口处理方法 定义一个动态的元素,当我们的base app 需要加载子模块的时候,会渲染出子模块需要渲染元素.
我们的子模块找到了自己模块需要渲染的节点的时候,就会渲染出来.
const domElementGetterStr = ` function domElementGetter() { let el = document.getElementById('submodule-page') if (!el) { el = document.createElement('div') el.id = 'submodule-page' } let timer = null timer = setInterval(() =&gt; { if (document.</description>
</item>
<item>
<title>记一次MongoDB迁移 - 备份与恢复</title>
<link>https://alili.tech/archive/7815p445rtf/</link>
<pubDate>Wed, 27 Mar 2019 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/7815p445rtf/</guid>
<description> 最近对自己的的爬虫数据库做了一次迁移,所以在这里记录一下mongodb的备份与恢复
MongoDB数据备份 mongodump -h dbhost -d dbname -o dbdirectory -h: MongDB所在服务器地址,例如:127.0.0.1,如果没有改的话一般是:127.0.0.1:27017
-d: 需要备份的数据库名字,例如:test
-o: 备份的数据存放位置,例如:/home/root/xxx,
MongoDB数据恢复 mongorestore -h &lt;hostname&gt;&lt;:port&gt; -d dbname &lt;path&gt; &ndash;host &lt;:port&gt;, -h &lt;:port&gt;: MongoDB所在服务器地址,默认为: localhost:27017
&ndash;db , -d : 需要备份的数据库名字,例如:test
&ndash;drop: 先删除现有的数据,再恢复数据
: 指定备份文件的目录
&ndash;dir: 指定备份文件的目录,不能同时指定 和 &ndash;dir 选项。
</description>
</item>
<item>
<title>一个两年前.gitattributes文件导致的bug</title>
<link>https://alili.tech/archive/zift8zkisnf/</link>
<pubDate>Thu, 24 Jan 2019 19:33:33 +0000</pubDate>
<guid>https://alili.tech/archive/zift8zkisnf/</guid>
<description>起因 一时兴起,我打开了我两年前写的一个前端项目开始代码审查. 这个项目我一个人写了两年,大概十几万行代码的样子.
发现这个项目的git居然把所有的代码文件都识别成了二进制文件. 这个问题,之前困扰了我很久,却一直找不到原因.导致我的代码没有对比记录.
再顽固的问题也有水落石出的一天
说巧不巧,今天偏偏找到了问题的源头.
.gitattributes 最后发现是因为.gitattributes文件有一行配置因为换行问题.导致所有text的文件都识别成了二进制文件.
.gitattributes的作用 .gitattributes文件是一个简单的text文本文件,它的作用是重新定义指定文件的属性, 指定非文本文件的对比合并方式
编写规则 pattern attr1 attr2 ... 示例 *.ttf binary *.woff binary *.eot binary *.otf binary * text=auto 上面这段代码表示,将一些字体文件指定为二进制文件,当提交代码的时候git不会diff这些文件的变动详情. 他只会告诉你,文件变动了,但是不会告诉你具体哪里变了.</description>
</item>
<item>
<title>LintCode 最长公共前缀</title>
<link>https://alili.tech/archive/an7l200dx1w/</link>
<pubDate>Sat, 19 Jan 2019 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/an7l200dx1w/</guid>
<description>编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 &ldquo;&ldquo;。
示例 1: 输入: [&quot;flower&quot;,&quot;flow&quot;,&quot;flight&quot;] 输出: &quot;fl&quot; 示例 2: 输入: [&quot;dog&quot;,&quot;racecar&quot;,&quot;car&quot;] 输出: &quot;&quot; 解释: 输入不存在公共前缀。 说明:
所有输入只包含小写字母 a-z 。
Javascript /** * @param {string[]} strs * @return {string} */ var longestCommonPrefix = function(strs) { if(strs.length === 0)return &quot;&quot; let current = &quot;&quot; let prefix = &quot;&quot; let index = 0; let isTrue = true; while (strs[0][index]) { //以第一个单词为基准 current = strs[0][index]; // 以第一个单词为基准 for(let i = 0; i &lt; strs.</description>
</item>
<item>
<title>LintCode 买卖股票的最佳时机 II</title>
<link>https://alili.tech/archive/n15zv1hxje/</link>
<pubDate>Sat, 12 Jan 2019 00:00:00 +0000</pubDate>
<guid>https://alili.tech/archive/n15zv1hxje/</guid>
<description>给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1: 输入: [7,1,5,3,6,4] 输出: 7 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。 示例 2: 输入: [1,2,3,4,5] 输出: 4 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 示例 3: 输入: [7,6,4,3,1] 输出: 0 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。 Javascript /** * @param {number[]} prices * @return {number} */ var maxProfit = function(prices) { let n = 0; prices.</description>
</item>
<item>
<title>Canvas 处理跨域图片终极方案</title>
<link>https://alili.tech/archive/g6kghla5ugc/</link>
<pubDate>Thu, 10 Jan 2019 16:14:25 +0000</pubDate>
<guid>https://alili.tech/archive/g6kghla5ugc/</guid>
<description>浏览器因为安全问题,是不允许Canvas使用 getImageDatatoDataURL 处理跨域获取的图片的.
传统解决方法 1.服务器需要配置Access-Control-Allow-Origin 相信这一行代码,大家见过太多,我就赘述了
header(&quot;Access-Control-Allow-Origin: 你的域名&quot;); 2.设置crossOrigin属性 var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var img = new Image(); img.crossOrigin = 'anonymous'; //就是这行代码 img.onload = function () { context.drawImage(this, 0, 0); context.getImageData(0, 0, this.width, this.height); }; img.src = 'https://avatars3.xxxx.com/u/496048'; 到这里,你的跨域问题已经基本解决. 但是因为一些客观因素后端工程师拒绝帮你设置跨域头,那你该怎么办?
终极解决方案
大致思路是 使用ajax获取到图片,然后使用 FileReader转成base64;再使用canvas处理base64格式的图片就好了.
获取图片的base64 function getBase64(imgUrl) { return new Promise(((resolve, reject) =&gt; { window.URL = window.URL || window.webkitURL; // 声明一个XMLHttpRequest const xhr = new XMLHttpRequest(); // 获取图片 xhr.</description>
</item>
<item>
<title>Git大小写不敏感导致的烦人问题</title>
<link>https://alili.tech/archive/rrc7ngyr5rp/</link>
<pubDate>Wed, 09 Jan 2019 19:33:33 +0000</pubDate>
<guid>https://alili.tech/archive/rrc7ngyr5rp/</guid>
<description>因为同事把一个文件夹的小写改成了大写,导致我本地的提交不能直接push rebase 等一些列操作.恼的很~
error: The following untracked working tree files would be overwritten by checkout: xxx.js Please move or remove them before you can switch branches. Aborting 解决办法 设置为大小写敏感(问题解决后,一定要还原配置)
git config core.ignorecase false 尽管设置大小写敏感之后,始终会影响其他分支的代码,所以不建议一直使用这个配置,当问题解决之后,还是要改回去.
改回默认 git config core.ignorecase true # or git config --unset core.ignorecase 尾巴 平时编码的时候,还是千万不要直接重命名大小写,自己本地看起来没有什么问题.在别人问题上却是大问题.
如果发生了类似问题的,应该在源头解决(谁机器上重命名的,谁改回去).如果不解决的,应该拖出去打~~</description>
</item>
<item>
<title>Visual Studio Code 下的 jsconfig.json</title>
<link>https://alili.tech/archive/ltucv5fyj9/</link>
<pubDate>Tue, 08 Jan 2019 19:33:33 +0000</pubDate>
<guid>https://alili.tech/archive/ltucv5fyj9/</guid>
<description>今天聊聊 Visual Studio Code 的jsconfig.json配置小技巧.
jsconfig.json 是什么? 如果你的项目中有一个 jsconfig.json文件的话,这个文件的配置可以对你的文件所在目录下的所有js代码做出个性化支持.
Tip: 如果你在项目中新加了这个文件,记得重启一下vscode哦~
例子 exclude 属性 当vscode扫描项目代码的时候,如果遇到了node_module的话是会变得很慢的.
如果想要编辑器的性能有一个提升的话,我们应该排除这个文件夹.
{ &quot;exclude&quot;: [ &quot;node_modules&quot; ] } include 属性 当然还有相对的include属性
{ &quot;include&quot;: [ &quot;src/**/*&quot; ] } webpack aliases 的支持 如果我们在我们的webpack里面配置的路径的别名,心细的小朋友就发现了. 我们的vscode不能根据别名路径导入的包跳转文件了.所以我们还需要jsconfig.json的支持.
{ &quot;compilerOptions&quot;: { &quot;baseUrl&quot;: &quot;.&quot;, &quot;paths&quot;: { &quot;@component&quot;: [&quot;./src/component&quot;] } } } jsconfig.json的配置是tsconfig.json的子集,
以后有机会的话聊一聊tsconfig.json.
今天就到这里~</description>
</item>
<item>
<title>前端面试知识点收集 - JavaScript篇</title>
<link>https://alili.tech/archive/6kodkbztk2h/</link>
<pubDate>Mon, 07 Jan 2019 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/6kodkbztk2h/</guid>
<description>JavaScript 面试知识点总结 1. 介绍 js 的基本数据类型。 js 一共有六种基本数据类型,分别是 Undefined、Null、Boolean、Number、String,还有在 ES6 中新增的 Symbol 类型, 代表创建后独一无二且不可变的数据类型,它的出现我认为主要是为了解决可能出现的全局变量冲突的问题。 2. JavaScript 有几种类型的值?你能画一下他们的内存图吗? 涉及知识点:
栈:原始数据类型(Undefined、Null、Boolean、Number、String) 堆:引用数据类型(对象、数组和函数) 两种类型的区别是:存储位置不同。 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在 栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实 体。 回答:
js 可以分为两种类型的值,一种是基本数据类型,一种是复杂数据类型。 基本数据类型....(参考1) 复杂数据类型指的是 Object 类型,所有其他的如 Array、Date 等数据类型都可以理解为 Object 类型的子类。 两种类型间的主要区别是它们的存储位置不同,基本数据类型的值直接保存在栈中,而复杂数据类型的值保存在堆中,通过使用在栈中 保存对应的指针来获取堆中的值。 详细资料可以参考: 《JavaScript 有几种类型的值?》 《JavaScript 有几种类型的值?能否画一下它们的内存图;》
3. 什么是堆?什么是栈?它们之间有什么区别和联系? 堆和栈的概念存在于数据结构中和操作系统内存中。 在数据结构中,栈中数据的存取方式为先进后出。而堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。完全 二叉树是堆的一种实现方式。 在操作系统中,内存被分为栈区和堆区。 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 堆区内存一般由程序员分配释放,若程序员不释放,程序结束时可能由垃圾回收机制回收。 详细资料可以参考: 《什么是堆?什么是栈?他们之间有什么区别和联系?》
4. 内部属性 [[Class]] 是什么? 所有 typeof 返回值为 &quot;object&quot; 的对象(如数组)都包含一个内部属性 [[Class]](我们可以把它看作一个内部的分类,而非 传统的面向对象意义上的类)。这个属性无法直接访问,一般通过 Object.</description>
</item>
<item>
<title>前端面试知识点收集 - HTML篇</title>
<link>https://alili.tech/archive/aco9rw01fld/</link>
<pubDate>Sun, 06 Jan 2019 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/aco9rw01fld/</guid>
<description>HTML 面试知识点总结 1. DOCTYPE 的作用是什么? 相关知识点:
IE5.5 引入了文档模式的概念,而这个概念是通过使用文档类型(DOCTYPE)切换实现的。 &lt;!DOCTYPE&gt;声明位于 HTML 文档中的第一行,处于 &lt;html&gt; 标签之前。告知浏览器的解析器用什么文档标准解析这个文档。 DOCTYPE 不存在或格式不正确会导致文档以兼容模式呈现。 回答(参考1-5):
&lt;!DOCTYPE&gt; 声明一般位于文档的第一行,它的作用主要是告诉浏览器以什么样的模式来解析文档。一般指定了之后会以标准模式来 进行文档解析,否则就以兼容模式进行解析。在标准模式下,浏览器的解析规则都是按照最新的标准进行解析的。而在兼容模式下,浏 览器会以向后兼容的方式来模拟老式浏览器的行为,以保证一些老的网站的正确访问。 在 html5 之后不再需要指定 DTD 文档,因为 html5 以前的 html 文档都是基于 SGML 的,所以需要通过指定 DTD 来定义文 档中允许的属性以及一些规则。而 html5 不再基于 SGML 了,所以不再需要使用 DTD。 2. 标准模式与兼容模式各有什么区别? 标准模式的渲染方式和 JS 引擎的解析方式都是以该浏览器支持的最高标准运行。在兼容模式中,页面以宽松的向后兼容的方式显示 ,模拟老式浏览器的行为以防止站点无法工作。 3. HTML5 为什么只需要写 &lt;!DOCTYPE HTML&gt;,而不需要引入 DTD? HTML5 不基于 SGML,因此不需要对 DTD 进行引用,但是需要 DOCTYPE 来规范浏览器的行为(让浏览器按照它们应该的方式来运 行)。 而 HTML4.01 基于 SGML ,所以需要对 DTD 进行引用,才能告知浏览器文档所使用的文档类型。 4.</description>
</item>
<item>
<title>前端面试知识点收集 - CSS篇</title>
<link>https://alili.tech/archive/63l6rtnya2e/</link>
<pubDate>Sat, 05 Jan 2019 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/63l6rtnya2e/</guid>
<description>CSS 面试知识点总结 1.介绍一下标准的 CSS 的盒子模型?低版本 IE 的盒子模型有什么不同的? 相关知识点:
(1)有两种盒子模型:IE盒模型(border-box)、W3C标准盒模型(content-box) (2)盒模型:分为内容(content)、填充(padding)、边界(margin)、边框(border)四个部分 IE盒模型和W3C标准盒模型的区别: (1)W3C标准盒模型:属性width,height只包含内容content,不包含border和padding (2)IE盒模型:属性width,height包含content、border和padding,指的是content +padding+border。 在ie8+浏览器中使用哪个盒模型可以由box-sizing(CSS新增的属性)控制,默认值为content-box,即标准盒模型; 如果将box-sizing设为border-box则用的是IE盒模型。如果在ie6,7,8中DOCTYPE缺失会将盒子模型解释为IE 盒子模型。若在页面中声明了DOCTYPE类型,所有的浏览器都会把盒模型解释为W3C盒模型。 回答:
盒模型都是由四个部分组成的,分别是margin、border、padding和content。 标准盒模型和IE盒模型的区别在于设置width和height时,所对应的范围不同。标准盒模型的width和height属性的 范围只包含了content,而IE盒模型的width和height属性的范围包含了border、padding和content。 一般来说,我们可以通过修改元素的box-sizing属性来改变元素的盒模型。 详细的资料可以参考: 《CSS 盒模型详解》
2.CSS 选择符有哪些? (1)id选择器(#myid) (2)类选择器(.myclassname) (3)标签选择器(div,h1,p) (4)后代选择器(h1 p) (5)相邻后代选择器(子)选择器(ul&gt;li) (6)兄弟选择器(li~a) (7)相邻兄弟选择器(li+a) (8)属性选择器(a[rel=&quot;external&quot;]) (9)伪类选择器(a:hover,li:nth-child) (10)伪元素选择器(::before、::after) (11)通配符选择器(*) 3.::before 和:after 中双冒号和单冒号有什么区别?解释一下这 2 个伪元素的作用。 相关知识点:
单冒号(:)用于CSS3伪类,双冒号(::)用于CSS3伪元素。(伪元素由双冒号和伪元素名称组成) 双冒号是在当前规范中引入的,用于区分伪类和伪元素。不过浏览器需要同时支持旧的已经存在的伪元素写法, 比如:first-line、:first-letter、:before、:after等, 而新的在CSS3中引入的伪元素则不允许再支持旧的单冒号的写法。 想让插入的内容出现在其它内容前,使用::before,否者,使用::after; 在代码顺序上,::after生成的内容也比::before生成的内容靠后。 如果按堆栈视角,::after生成的内容会在::before生成的内容之上。 回答:
在css3中使用单冒号来表示伪类,用双冒号来表示伪元素。但是为了兼容已有的伪元素的写法,在一些浏览器中也可以使用单冒号 来表示伪元素。 伪类一般匹配的是元素的一些特殊状态,如hover、link等,而伪元素一般匹配的特殊的位置,比如after、before等。 4.伪类与伪元素的区别 css引入伪类和伪元素概念是为了格式化文档树以外的信息。也就是说,伪类和伪元素是用来修饰不在文档树中的部分,比如,一句 话中的第一个字母,或者是列表中的第一个元素。 伪类用于当已有的元素处于某个状态时,为其添加对应的样式,这个状态是根据用户行为而动态变化的。比如说,当用户悬停在指定的 元素时,我们可以通过:hover来描述这个元素的状态。 伪元素用于创建一些不在文档树中的元素,并为其添加样式。它们允许我们为元素的某些部分设置样式。比如说,我们可以通过::be fore来在一个元素前增加一些文本,并为这些文本添加样式。虽然用户可以看到这些文本,但是这些文本实际上不在文档树中。 有时你会发现伪元素使用了两个冒号(::)而不是一个冒号(:)。这是CSS3的一部分,并尝试区分伪类和伪元素。大多数浏览 器都支持这两个值。按照规则应该使用(::)而不是(:),从而区分伪类和伪元素。但是,由于在旧版本的W3C规范并未对此进行 特别区分,因此目前绝大多数的浏览器都支持使用这两种方式表示伪元素。 详细资料可以参考: 《总结伪类与伪元素》</description>
</item>
<item>
<title>前端面试知识点收集 - 综合篇</title>
<link>https://alili.tech/archive/7bp4sppqtsy/</link>
<pubDate>Fri, 04 Jan 2019 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/7bp4sppqtsy/</guid>
<description>$HTML, HTTP,web 综合问题 常见排序算法的时间复杂度,空间复杂度 前端需要注意哪些 SEO 合理的 title、description、keywords:搜索对着三项的权重逐个减小,title 值强调重点即可,重要关键词出现不要超过 2 次,而且要靠前,不同页面 title 要有所不同;description 把页面内容高度概括,长度合适,不可过分堆砌关键词,不同页面 description 有所不同;keywords 列举出重要关键词即可 语义化的 HTML 代码,符合 W3C 规范:语义化代码让搜索引擎容易理解网页 重要内容 HTML 代码放在最前:搜索引擎抓取 HTML 顺序是从上到下,有的搜索引擎对抓取长度有限制,保证重要内容一定会被抓取 重要内容不要用 js 输出:爬虫不会执行 js 获取内容 少用 iframe:搜索引擎不会抓取 iframe 中的内容 非装饰性图片必须加 alt 提高网站速度:网站速度是搜索引擎排序的一个重要指标 web 开发中会话跟踪的方法有哪些 cookie session url 重写 隐藏 input ip 地址 &lt;img&gt;的title和alt有什么区别 title是global attributes之一,用于为元素提供附加的 advisory information。通常当鼠标滑动到元素上的时候显示。 alt是&lt;img&gt;的特有属性,是图片内容的等价描述,用于图片无法加载时显示、读屏器阅读图片。可提图片高可访问性,除了纯装饰图片外都必须设置有意义的值,搜索引擎会重点分析。 doctype 是什么,举例常见 doctype 及特点 &lt;!doctype&gt;声明必须处于 HTML 文档的头部,在&lt;html&gt;标签之前,HTML5 中不区分大小写 &lt;!</description>
</item>
<item>
<title>幕后英雄Abstract syntax tree抽象语法树</title>
<link>https://alili.tech/archive/9rfliipfkip/</link>
<pubDate>Thu, 03 Jan 2019 17:33:33 +0000</pubDate>
<guid>https://alili.tech/archive/9rfliipfkip/</guid>
<description>抽象语法树 AST 在计算机科学中,抽象语法树,或简称语法树,是源代码语法结构的一种抽象表示.
这里介绍一个网站 https://astexplorer.net ,可以让你清晰的看到一段js,css以及其他语言被转换成语法树的样子.
在我们的浏览器中,也会对我们的js代码解析成抽象语法书然后再进一步的分析以及下一步的操作.
语法树就前端而言,被利用在各种地方,代码语法的检查,代码风格的检查,代码的格式化,代码的高亮,代码错误提示,代码自动补全,比如:
eslint JSHint babel webpack rollup UglifyJS2 等等等等&hellip; 根本数不过来.
举个例子 一段代码被解析成语法树后的样子 目前业界对抽象语法树是有规范的, 感兴趣的朋友可以查看这个网址: SpiderMonkey
let str ='我是字符串'; to AST
{ &quot;type&quot;: &quot;Program&quot;, &quot;start&quot;: 0, &quot;end&quot;: 17, &quot;body&quot;: [ { &quot;type&quot;: &quot;VariableDeclaration&quot;, &quot;start&quot;: 0, &quot;end&quot;: 17, &quot;declarations&quot;: [ { &quot;type&quot;: &quot;VariableDeclarator&quot;, &quot;start&quot;: 4, &quot;end&quot;: 16, &quot;id&quot;: { &quot;type&quot;: &quot;Identifier&quot;, &quot;start&quot;: 4, &quot;end&quot;: 7, &quot;name&quot;: &quot;str&quot; }, &quot;init&quot;: { &quot;type&quot;: &quot;Literal&quot;, &quot;start&quot;: 9, &quot;end&quot;: 16, &quot;value&quot;: &quot;我是字符串&quot;, &quot;raw&quot;: &quot;'我是字符串'&quot; } } ], &quot;kind&quot;: &quot;let&quot; } ], &quot;sourceType&quot;: &quot;module&quot; } Nodejs解析抽象语法树的工具 常用js解析工具 esprima traceur acorn shift 使用esprima举个例子 # node环境 &gt; var esprima = require('esprima'); &gt; var program = 'const answer = 42'; &gt; esprima.</description>
</item>
<item>
<title>2018 年终有感</title>
<link>https://alili.tech/archive/53ogpu5lzuh/</link>
<pubDate>Sat, 29 Dec 2018 21:29:00 +0000</pubDate>
<guid>https://alili.tech/archive/53ogpu5lzuh/</guid>
<description>今天是今年的最后一个工作日,现在我在高铁上.已经习惯在高铁上用电脑折腾点什么. 回想起来,不知道在高铁上发过多少次版本,fix掉了多少个bug.啊哈哈~
回看今年的2018,对于我来说是传奇的,是曲折的,甚至是有点丧的.幸运的是个人成长并没有客观原因而耽误.
我的团队 回想到今年7月份,亲手组建的前端团队因为大环境的原因而解散,确实是很痛心的. 如果按照正常来发展,我想应该可以打造成我心目中的Dream Team了吧.至少各个对技术是那么的有热情.
成长要与时间赛跑 不过一切都没有关系,今年也不过刚满26,以后有的是机会.我经常这样安慰我自己.恐怕再过两年,这就不是自我安慰的理由了. 唯一要做的就是个人成长必须要与时间赛跑.
今年去参加ng china的时候,让我非常有印象的一场演讲是雪狼的. 其中有一段大意是根据我们现在信息爆炸的年代,我们的成长应该古人早十年:
二十而立
三十不惑
四十知天命
五十耳顺
六十从心所欲
对此,我是十份赞同的.
焦虑的一年 2018年对于我来说,是焦虑的一年.因为自身的焦虑,促使自己每天都在不断学习与进步~ 当然今年也错过了很多不错的机会.现在想起来,还是自己太年轻了,没有见过大人物,看到偶像立马就怂了. 不过还是随时整装待发,随时面对新挑战.
关于博客 今年博客可以说是大升级了,界面看起来差不多,但是内部基本上重写了一遍. 还专门做了SEO,甚至还跑到 segmentfault 去打了一个星期的广告,哈哈哈~ 虽然宣传效果没有达到预期~
以前没有专门的做统计,自10月上线统计以来,看了一下网站的总访问量已经到达了10W+. 我不知道其他的博客主的网站数据是怎么样的,但是按照现在趋势. 访问量还是在缓慢的增加.每天看到那些统计数据,还是非常有意思的.
从之前每周强制写一篇,到现在的每周强制自己写三篇.写一些自己正在做的,看到的,有意思的技术总结.有时候是一些很小的技术点,有一些也是我研究了很久,有一定系统性的东西.写博客对我来说是一种非常有效率的学习方式.以后应该还会坚持.不过也说不上坚持吧,渐渐的已经成为了一种爱好与习惯. 希望这种习惯,可以让自己的技能可以有发挥的地方.有一个组织与自己互利互惠,相互成长,得到各自想要的东西.
关于家庭 关于家庭,今年1月份我儿子小可乐出生了,直到小可乐出生很久,我都没能让自己非常喜欢小孩. 不知道是因为自己是独生子或者从来没有跟自己年龄小的人相处的原因. 现在看,我身边的朋友大多都比我大个五六七八岁的. 但是现在,相处的越久发现这种与儿子的羁绊越来越深.希望以后可以跟他成为好朋友,甚至是好兄弟,好导师.
至少我们现在看到的世界,比我们父辈的那一代要大很多. 我们要学习父辈的智慧,重新看待与审视这个世界. 智慧这个东西,很有意思~ 不管放在哪个时代都是可以直接复用的.
关于技术 今年我有听到一些话,让我深深的陷入思考.我一同事跟我说: &ldquo;我对技术没有你那么高的热情,我只是为了谋生&rdquo;. 我很排斥这种说法,但是又不得不承认很多人是这样. 这是不是很多时候面试,别人问你是不是计算机专业出身的一样.
我问自己,我学习这么多的知识是谋生吗? 最终我想了想,得到了答案: 学习这些技术是一个让我理解这个世界的途径. 我想我能有一个这样的理解,是因为我比较幸运吧~ 虽然生活压力还是有的,但也没有那么大~是吧? (突然脑子想到那个段子~)
我一直觉得我有一个问题,我眼中总是只能看到比我优秀的人,看着他们如此优秀,让自己无比焦虑.
有时候我对自己说,我是不是偶尔也向后看看? 可以自己释怀很多~ 但是很多时候又觉得自己办不到? 这算不算习惯性见贤思齐的一种表现~ 我觉得这是病,时间久了多多少少还是会有一些问题的.
关于2019 2019我觉得没有什么可计划的,要是真有计划那也是把2018年一直坚持的事情继续做下去.</description>
</item>
<item>
<title>聊聊HTTP的X-Forwarded-For 和 X-Real-IP</title>
<link>https://alili.tech/archive/izbidk3gu3s/</link>
<pubDate>Sat, 29 Dec 2018 16:14:25 +0000</pubDate>
<guid>https://alili.tech/archive/izbidk3gu3s/</guid>
<description>最近在看网易云音乐API nodejs这个项目的文档的时候.
发现调用须知里,有一段是这样的说的:
由于网易限制,此项目在国外服务器上使用会受到限制,如需解决 , 可使用大陆服务器或者使用代理 , 感谢 @hiyangguo提出的解决方法: 在 &lsquo;util.js&rsquo; 的 &lsquo;headers&rsquo; 处增加 X-Real-IP&rsquo;:&lsquo;211.161.244.70&rsquo; // 任意国内 IP 即可解决
网易云音乐的接口调用对国外加了限制,想要跳过这样的限制的话,就必须在headers里修改X-Real-IP 就可以解决.
那X-Real-IP是什么?是干什么用的?
X-Real-IP X-Real-IP,这是一个自定义头部字段。X-Real-IP 通常被 HTTP 代理用来表示与它产生 TCP 连接的设备 IP,这个设备可能是其他代理,也可能是真正的请求端。需要注意的是,X-Real-IP 目前并不属于任何标准,
跟他非常相关的,还有一个X-Forwarded-For的自定义头部字段.
X-Forwarded-For X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入 RFC 7239(Forwarded HTTP Extension)标准之中。
格式:
X-Forwarded-For: client, proxy1, proxy2 如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:
X-Forwarded-For: IP0, IP1, IP2 X-Forwarded-For 会记录用户ip与每次转发用的代理服务器ip.</description>
</item>
<item>
<title>Angular路径别名配置</title>
<link>https://alili.tech/archive/3u54dxnerfn/</link>
<pubDate>Thu, 27 Dec 2018 22:17:36 +0000</pubDate>
<guid>https://alili.tech/archive/3u54dxnerfn/</guid>
<description>angular cli 内置了 webpack,所以也是可以配置路径别名的. 而且配置的方式几乎是一样的.
配置路径别名 找到你的项目根目录的 tsconfig.json 文件.
注意: 如果你的配置不生效,需要查看你的baseUrl是否配置正确.
{ &quot;compilerOptions&quot;: { &quot;baseUrl&quot;: &quot;./src/&quot;, &quot;paths&quot;: { &quot;@app/*&quot;: [&quot;app/*&quot;], &quot;@services/*&quot;: [&quot;app/services/*&quot;] } } } 配置之前 import { Api } from '../../../../../services/api.service'; import { xxx } from '../../../../../services/api.xxx'; 配置之后 import { Api } from '@services/api.service'; import { xxx } from '@services/api.xxx'; 一些开发中的小技巧,希望可以帮到你.</description>
</item>
<item>
<title>Typescript的福音:Json To Interface</title>
<link>https://alili.tech/archive/ijfdh4ry66c/</link>
<pubDate>Tue, 25 Dec 2018 19:33:33 +0000</pubDate>
<guid>https://alili.tech/archive/ijfdh4ry66c/</guid>
<description> 在我们使用ts的时候,我们需要写大量的 Interface.
但是我们一条json数据的字段实在是太多了,一个一个写的话不仅会花费大量的时间.
在团队内部推广Typescript,因为使用问题导致推广层层阻碍,最后放弃使用屡见不鲜.
今天推荐的这个插件,希望可以帮到你.
JSON to TS 插件 安装 ext install json-to-ts 复制到剪贴板后 运行快捷键 (Ctrl + Alt + V) 鼠标选中后 运行快捷键 (Ctrl + Alt + S) 已知问题 linux 已知问题 Command failed: xclip -selection clipboard -o 解决方法 sudo apt-get install xclip </description>
</item>
<item>
<title>Centos7 如何使用yum升级git到最新版本</title>
<link>https://alili.tech/archive/cue4m2rioxl/</link>
<pubDate>Mon, 24 Dec 2018 20:32:05 +0000</pubDate>
<guid>https://alili.tech/archive/cue4m2rioxl/</guid>