-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathHomeContent.js
2774 lines (2696 loc) · 118 KB
/
HomeContent.js
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
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*/
import {
createContext,
memo,
useState,
useContext,
useId,
Suspense,
useEffect,
useRef,
useTransition,
} from 'react';
import cn from 'classnames';
import NextLink from 'next/link';
import ButtonLink from '../ButtonLink';
import {IconRestart} from '../Icon/IconRestart';
import BlogCard from 'components/MDX/BlogCard';
import {IconChevron} from 'components/Icon/IconChevron';
import {IconSearch} from 'components/Icon/IconSearch';
import {Logo} from 'components/Logo';
import Link from 'components/MDX/Link';
import CodeBlock from 'components/MDX/CodeBlock';
import {ExternalLink} from 'components/ExternalLink';
import sidebarBlog from '../../sidebarBlog.json';
import * as React from 'react';
import Image from 'next/image';
function Section({children, background = null}) {
return (
<div
className={cn(
'mx-auto flex flex-col w-full',
background === null && 'max-w-7xl',
background === 'left-card' &&
'bg-gradient-left dark:bg-gradient-left-dark border-t border-primary/10 dark:border-primary-dark/10 ',
background === 'right-card' &&
'bg-gradient-right dark:bg-gradient-right-dark border-t border-primary/5 dark:border-primary-dark/5'
)}
style={{
contain: 'content',
}}>
<div className="flex-col gap-2 flex grow w-full my-20 lg:my-32 mx-auto items-center">
{children}
</div>
</div>
);
}
function Header({children}) {
return (
<h2 className="leading-xl font-display text-primary dark:text-primary-dark font-semibold text-5xl lg:text-6xl -mt-4 mb-7 w-full max-w-3xl lg:max-w-xl">
{children}
</h2>
);
}
function Para({children}) {
return (
<p className="max-w-3xl mx-auto text-lg lg:text-xl text-secondary dark:text-secondary-dark leading-normal">
{children}
</p>
);
}
function Center({children}) {
return (
<div className="px-5 lg:px-0 max-w-4xl lg:text-center text-white text-opacity-80 flex flex-col items-center justify-center">
{children}
</div>
);
}
function FullBleed({children}) {
return (
<div className="max-w-7xl mx-auto flex flex-col w-full">{children}</div>
);
}
function CurrentTime() {
const [date, setDate] = useState(new Date());
const currentTime = date.toLocaleTimeString([], {
hour: 'numeric',
minute: 'numeric',
});
useEffect(() => {
const msPerMinute = 60 * 1000;
let nextMinute = Math.floor(+date / msPerMinute + 1) * msPerMinute;
const timeout = setTimeout(() => {
if (Date.now() > nextMinute) {
setDate(new Date());
}
}, nextMinute - Date.now());
return () => clearTimeout(timeout);
}, [date]);
return <span suppressHydrationWarning>{currentTime}</span>;
}
const blogSidebar = sidebarBlog.routes[1];
if (blogSidebar.path !== '/blog') {
throw Error('Could not find the blog route in sidebarBlog.json');
}
const recentPosts = blogSidebar.routes.slice(0, 4).map((entry) => ({
title: entry.titleForHomepage,
icon: entry.icon,
date: entry.date,
url: entry.path,
}));
export function HomeContent() {
return (
<>
<div className="ps-0">
<div className="mx-5 mt-12 lg:mt-24 mb-20 lg:mb-32 flex flex-col justify-center">
<div className="uwu-visible flex justify-center">
<Image
alt="logo by @sawaratsuki1004"
title="logo by @sawaratsuki1004"
loading="eager"
width={313}
height={160}
src="/images/uwu.png"
/>
</div>
<Logo
className={cn(
'uwu-hidden mt-4 mb-3 text-brand dark:text-brand-dark w-24 lg:w-28 self-center text-sm me-0 flex origin-center transition-all ease-in-out'
)}
/>
<h1 className="uwu-hidden text-5xl font-display lg:text-6xl self-center flex font-semibold leading-snug text-primary dark:text-primary-dark">
React
</h1>
<p className="text-4xl font-display max-w-lg md:max-w-full py-1 text-center text-secondary dark:text-primary-dark leading-snug self-center">
用于构建 Web 和原生交互界面的库
</p>
<div className="mt-5 self-center flex gap-2 w-full sm:w-auto flex-col sm:flex-row">
<ButtonLink
href={'/learn'}
type="primary"
size="lg"
className="w-full sm:w-auto justify-center"
label="Learn React">
学习 React
</ButtonLink>
<ButtonLink
href={'/reference/react'}
type="secondary"
size="lg"
className="w-full sm:w-auto justify-center"
label="API Reference">
API 参考
</ButtonLink>
</div>
</div>
<Section background="left-card">
<Center>
<Header>用组件创建用户界面</Header>
<Para>
React 让你可以通过组件来构建用户界面。你可以创建像{' '}
<Code>Thumbnail</Code>、<Code>LikeButton</Code> 和{' '}
<Code>Video</Code> 这样的组件。然后将它们组合成整个应用程序。
</Para>
</Center>
<FullBleed>
<Example1 />
</FullBleed>
<Center>
<Para>
无论你是独自工作还是与成千上万的其他开发人员合作,使用 React
的感觉都是相同的。它旨在让你轻松地组合由独立开发者、团队或组织编写的组件。
</Para>
</Center>
</Section>
<Section background="right-card">
<Center>
<Header>用代码和标签编写组件</Header>
<Para>
React 组件是 JavaScript 函数。想要有条件地显示一些内容吗?使用{' '}
<Code>if</Code> 语句。
<br className="hidden lg:inline" />
想要展示一个列表?尝试使用数组的 <Code>map()</Code> 方法。学习
React 就是学习编程。
</Para>
</Center>
<FullBleed>
<Example2 />
</FullBleed>
<Center>
<Para>
这种标签语法被称为 JSX。它是由 React 推广的 JavaScript
语法扩展。将 JSX
标签与相关的渲染逻辑放在一起,使得创建、维护和删除 React
组件变得容易。
</Para>
</Center>
</Section>
<Section background="left-card">
<Center>
<Header>在任何地方添加交互</Header>
<Para>
React
组件接收数据并返回应该出现在屏幕上的内容。你可以通过响应交互(例如用户输入)向它们传递新数据。然后,React
将更新屏幕以匹配新数据。
</Para>
</Center>
<FullBleed>
<Example3 />
</FullBleed>
<Center>
<Para>
你也可以不用 React 去构建整个页面,而只是将 React 添加到现有的
HTML 页面中,在任何地方呈现交互式的 React 组件。
</Para>
<div className="flex justify-start w-full lg:justify-center">
<CTA
color="gray"
icon="code"
href="/learn/add-react-to-an-existing-project">
将 React 添加到你已有的页面中
</CTA>
</div>
</Center>
</Section>
<Section background="right-card">
<Center>
<Header>
使用框架
<br className="hidden lg:inline" />
进行全栈开发
</Header>
<Para>
React
是一个库。它允许你将组件放在一起,但不关注路由和数据获取。要使用
React 构建整个应用程序,我们建议使用像{' '}
<Link href="https://nextjs.org">Next.js</Link> 或{' '}
<Link href="https://remix.run">Remix</Link> 这样的全栈 React
框架。
</Para>
</Center>
<FullBleed>
<Example4 />
</FullBleed>
<Center>
<Para>
React
也是一种架构。实现它的框架可以在服务端甚至是构建阶段使用异步组件来获取数据,也可以从文件或数据库读取数据,并将其传递给交互式组件。
</Para>
<div className="flex justify-start w-full lg:justify-center">
<CTA
color="gray"
icon="framework"
href="/learn/start-a-new-react-project">
使用框架开始一个新项目
</CTA>
</div>
</Center>
</Section>
<Section background="left-card">
<div className="mx-auto flex flex-col w-full">
<div className="mx-auto max-w-4xl lg:text-center items-center px-5 flex flex-col">
<Header>博采众长</Header>
<Para>
人们因为不同的原因偏好 Web 应用或原生应用。React
让你使用相同的技能构建 Web
应用程序和原生应用程序。它依赖于每个平台独特的优势,使你的界面就像原生一样。
</Para>
</div>
<div className="max-w-7xl mx-auto flex flex-col lg:flex-row mt-16 mb-20 lg:mb-28 px-5 gap-20 lg:gap-5">
<div className="relative lg:w-6/12 flex">
<div className="absolute -bottom-8 lg:-bottom-10 z-10 w-full">
<WebIcons />
</div>
<BrowserChrome hasRefresh={false} domain="example.com">
<div className="relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-right" />
<div className="bg-wash relative h-14 w-full" />
<div className="relative flex items-start justify-center flex-col flex-1 pb-16 pt-5 gap-3 px-5 lg:px-10 lg:pt-8">
<h4 className="leading-tight text-primary font-semibold text-3xl lg:text-4xl">
不忘初心
</h4>
<p className="lg:text-xl leading-normal text-secondary">
人们期望网页加载速度更快。在服务器上,React
可以让你在获取数据的同时开始流式传输 HTML,在任何
JavaScript
代码加载之前逐步填充剩余内容。在客户端,即使是在渲染过程中,React
也会使用标准的 Web API 使 UI 快速响应。
</p>
</div>
</div>
</BrowserChrome>
</div>
<div className="relative lg:w-6/12 flex">
<div className="absolute -bottom-8 lg:-bottom-10 z-10 w-full">
<NativeIcons />
</div>
<figure className="mx-auto max-w-3xl h-auto">
<div className="p-2.5 bg-gray-95 dark:bg-black rounded-2xl shadow-nav dark:shadow-nav-dark">
<div className="bg-gradient-right dark:bg-gradient-right-dark px-3 sm:px-3 pb-12 lg:pb-20 rounded-lg overflow-hidden">
<div className="select-none w-full h-14 flex flex-row items-start pt-3 -mb-2.5 justify-between text-tertiary dark:text-tertiary-dark">
<span className="uppercase tracking-wide leading-none font-bold text-sm text-tertiary dark:text-tertiary-dark">
<CurrentTime />
</span>
<div className="gap-2 flex -mt-0.5">
<svg
width="16"
height="20"
viewBox="0 0 72 72"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M34.852 6.22836C35.973 5.76401 37.2634 6.02068 38.1214 6.87868L53.1214 21.8787C53.7485 22.5058 54.066 23.3782 53.9886 24.2617C53.9113 25.1451 53.447 25.9491 52.7205 26.4577L39.0886 36.0003L52.7204 45.5423C53.447 46.0508 53.9113 46.8548 53.9886 47.7383C54.066 48.6218 53.7485 49.4942 53.1214 50.1213L38.1214 65.1213C37.2634 65.9793 35.973 66.236 34.852 65.7716C33.731 65.3073 33.0001 64.2134 33.0001 63V40.2624L22.7205 47.4583C21.3632 48.4085 19.4926 48.0784 18.5424 46.721C17.5922 45.3637 17.9223 43.4931 19.2797 42.543L28.6258 36.0004L19.2797 29.4583C17.9224 28.5082 17.5922 26.6376 18.5424 25.2803C19.4925 23.9229 21.3631 23.5928 22.7204 24.5429L33.0001 31.7384V9C33.0001 7.78661 33.731 6.6927 34.852 6.22836ZM39.0001 43.2622L46.3503 48.4072L39.0001 55.7574V43.2622ZM39.0001 28.7382V16.2426L46.3503 23.5929L39.0001 28.7382Z"
fill="currentColor"
/>
</svg>
<svg
width="16"
height="20"
viewBox="0 0 72 72"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M9 27C9.82864 27 10.5788 26.664 11.1217 26.1209C11.2116 26.0355 11.3037 25.9526 11.397 25.871C11.625 25.6714 11.9885 25.3677 12.4871 24.9938C13.4847 24.2455 15.0197 23.219 17.0912 22.1833C21.2243 20.1167 27.5179 18 35.9996 18C44.4813 18 50.7748 20.1167 54.9079 22.1833C56.9794 23.219 58.5144 24.2455 59.5121 24.9938C59.6056 25.0639 60.8802 26.1233 60.8802 26.1233C61.423 26.6652 62.1724 27 63 27C64.6569 27 66 25.6569 66 24C66 22.8871 65.3475 22.0506 64.5532 21.3556C64.2188 21.0629 63.7385 20.6635 63.1121 20.1938C61.8597 19.2545 60.0197 18.031 57.5912 16.8167C52.7243 14.3833 45.5179 12 35.9996 12C26.4813 12 19.2748 14.3833 14.4079 16.8167C11.9794 18.031 10.1394 19.2545 8.88706 20.1938C8.26066 20.6635 7.78035 21.0629 7.44593 21.3556C7.2605 21.5178 7.07794 21.6834 6.9016 21.8555C6.33334 22.417 6 23.1999 6 24C6 25.6569 7.34315 27 9 27Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M26.1116 48.631C24.2868 50.4378 21 49.0661 21 46.5C21 45.6707 21.3365 44.92 21.8804 44.3769C21.9856 44.2702 22.0973 44.1695 22.209 44.0697C22.3915 43.9065 22.6466 43.6885 22.9713 43.4344C23.6195 42.9271 24.5536 42.2694 25.7509 41.6163C28.1445 40.3107 31.6365 39 35.9999 39C40.3634 39 43.8554 40.3107 46.249 41.6163C47.4463 42.2694 48.3804 42.9271 49.0286 43.4344C50.0234 44.213 51 45.134 51 46.5C51 48.1569 49.6569 49.5 48 49.5C47.1724 49.5 46.4231 49.1649 45.8803 48.623C45.7028 48.4617 45.5197 48.3073 45.3307 48.1594C44.9007 47.8229 44.2411 47.3556 43.3759 46.8837C41.6445 45.9393 39.1365 45 35.9999 45C32.8634 45 30.3554 45.9393 28.624 46.8837C27.7588 47.3556 27.0992 47.8229 26.6692 48.1594C26.3479 48.4109 26.155 48.5899 26.1116 48.631Z"
fill="currentColor"
/>
<path
d="M36 63C39.3137 63 42 60.3137 42 57C42 53.6863 39.3137 51 36 51C32.6863 51 30 53.6863 30 57C30 60.3137 32.6863 63 36 63Z"
fill="currentColor"
/>
<path
d="M15 39C13.3431 39 12 37.6569 12 36C12 34.3892 13.3933 33.3427 14.5534 32.4503C15.5841 31.6574 17.0871 30.6231 19.04 29.5952C22.9506 27.537 28.6773 25.5 35.9997 25.5C43.3222 25.5 49.0488 27.537 52.9595 29.5952C54.9123 30.6231 56.4154 31.6574 57.4461 32.4503C57.9619 32.847 58.361 33.1846 58.6407 33.4324C59.4024 34.1073 60 34.9345 60 36C60 37.6569 58.6569 39 57 39C56.1737 39 55.4255 38.6662 54.8829 38.1258C54.5371 37.7978 54.1653 37.4964 53.7878 37.206C52.9903 36.5926 51.7746 35.7519 50.165 34.9048C46.9506 33.213 42.1773 31.5 35.9997 31.5C29.8222 31.5 25.0488 33.213 21.8345 34.9048C20.2248 35.7519 19.0091 36.5926 18.2117 37.206C17.6144 37.6654 17.2549 37.9951 17.1459 38.098C16.5581 38.6591 15.8222 39 15 39Z"
fill="currentColor"
/>
</svg>
<svg
width="20"
height="20"
viewBox="0 0 72 72"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path
d="M12.9533 26.0038C13.224 24.7829 14.3285 24 15.579 24H50.421C51.6715 24 52.776 24.7829 53.0467 26.0038C53.4754 27.937 54 31.2691 54 36C54 40.7309 53.4754 44.063 53.0467 45.9962C52.776 47.2171 51.6715 48 50.421 48H15.579C14.3285 48 13.224 47.2171 12.9533 45.9962C12.5246 44.063 12 40.7309 12 36C12 31.2691 12.5246 27.937 12.9533 26.0038Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12.7887 15C8.77039 15 5.23956 17.668 4.48986 21.6158C3.74326 25.5473 3 30.7737 3 36C3 41.2263 3.74326 46.4527 4.48986 50.3842C5.23956 54.332 8.77039 57 12.7887 57H53.2113C57.2296 57 60.7604 54.332 61.5101 50.3842C61.8155 48.7765 62.1202 46.9522 62.3738 45H63.7918C64.5731 45 65.3283 44.8443 66 44.5491C67.2821 43.9857 68.2596 42.9142 68.5322 41.448C68.7927 40.0466 69 38.2306 69 36C69 33.7694 68.7927 31.9534 68.5322 30.552C68.2596 29.0858 67.2821 28.0143 66 27.4509C65.3283 27.1557 64.5731 27 63.7918 27H62.3738C62.1202 25.0478 61.8155 23.2235 61.5101 21.6158C60.7604 17.668 57.2296 15 53.2113 15H12.7887ZM53.2113 21H12.7887C11.3764 21 10.5466 21.8816 10.3845 22.7352C9.67563 26.4681 9 31.29 9 36C9 40.71 9.67563 45.5319 10.3845 49.2648C10.5466 50.1184 11.3764 51 12.7887 51H53.2113C54.6236 51 55.4534 50.1184 55.6155 49.2648C56.3244 45.5319 57 40.71 57 36C57 31.29 56.3244 26.4681 55.6155 22.7352C55.4534 21.8816 54.6236 21 53.2113 21Z"
fill="currentColor"
/>
</svg>
</div>
</div>
<div className="flex flex-col items-start justify-center pt-0 gap-3 px-2.5 lg:pt-8 lg:px-8">
<h4 className="leading-tight text-primary dark:text-primary-dark font-semibold text-3xl lg:text-4xl">
开发真正的原生应用
</h4>
<p className="h-full lg:text-xl text-secondary dark:text-secondary-dark leading-normal">
人们希望原生应用程序都有和自己使用的平台相一致的体验。
<Link href="https://reactnative.dev">
React Native
</Link>{' '}
和{' '}
<Link href="https://github.com/expo/expo">Expo</Link>{' '}
让你可以使用 React 构建 Android、iOS
等应用程序。它们的样式和体验都和原生应用程序一样,因为它们的用户界面是真正的原生用户界面。这不是一个
Web 视图——你的 React 组件由平台提供的真实 Android 或
iOS 视图来渲染。
</p>
</div>
</div>
</div>
</figure>
</div>
</div>
<div className="px-5 lg:px-0 max-w-4xl mx-auto lg:text-center text-secondary dark:text-secondary-dark">
<Para>
使用 React,你可以成为 Web 和原生应用的开发人员。
你的团队可以在不牺牲用户体验的情况下发布到各个平台。你的组织可以忽略平台差异,并拥有端到端的全部功能。
</Para>
<div className="flex justify-start w-full lg:justify-center">
<CTA color="gray" icon="native" href="https://reactnative.dev/">
为原生平台构建
</CTA>
</div>
</div>
</div>
</Section>
<Section background="right-card">
<div className="max-w-7xl mx-auto flex flex-col lg:flex-row px-5">
<div className="max-w-3xl lg:max-w-7xl gap-5 flex flex-col lg:flex-row lg:px-5">
<div className="w-full lg:w-6/12 max-w-3xl flex flex-col items-start justify-start lg:ps-5 lg:pe-10">
<Header>充分测试,安心升级</Header>
<Para>
React 非常谨慎地处理每个改动。每个 React
提交都在拥有超过十亿用户的关键业务页面上进行测试。Meta 的 10
万个 React 组件帮助验证每种迁移策略。
</Para>
<div className="order-last pt-5">
<Para>
React 团队始终在研究如何改进
React。一些研究需要数年才能得到回报。React
对将研究想法转化为生产具有高标准要求,只有经过证明的方法才能成为
React 的一部分。
</Para>
<div className="hidden lg:flex justify-start w-full">
<CTA color="gray" icon="news" href="/blog">
阅读更多 React 新闻
</CTA>
</div>
</div>
</div>
<div className="w-full lg:w-6/12">
<p className="uppercase tracking-wide font-bold text-sm text-tertiary dark:text-tertiary-dark flex flex-row gap-2 items-center mt-5 lg:-mt-2 w-full">
<IconChevron />
最新 React 新闻
</p>
<div className="flex-col sm:flex-row flex-wrap flex gap-5 text-start my-5">
<div className="flex-1 min-w-[40%] text-start">
<BlogCard {...recentPosts[0]} />
</div>
<div className="flex-1 min-w-[40%] text-start">
<BlogCard {...recentPosts[1]} />
</div>
<div className="flex-1 min-w-[40%] text-start">
<BlogCard {...recentPosts[2]} />
</div>
<div className="hidden sm:flex-1 sm:inline">
<BlogCard {...recentPosts[3]} />
</div>
</div>
<div className="flex lg:hidden justify-start w-full">
<CTA color="gray" icon="news" href="/blog">
阅读更多 React 新闻
</CTA>
</div>
</div>
</div>
</div>
</Section>
<Section background="left-card">
<div className="w-full">
<div className="mx-auto flex flex-col max-w-4xl">
<Center>
<Header>加入数百万人的社区</Header>
<Para>
你并不孤单。每个月有来自世界各地的两百万开发者访问 React
文档。React 可以让人们达成共识。
</Para>
</Center>
</div>
<CommunityGallery />
<div className="mx-auto flex flex-col max-w-4xl">
<Center>
<Para>
这就是为什么 React
不仅仅是一个库、一种架构,甚至不只是一个生态系统。React
是一个社区。在这里你可以寻求帮助,发现机会并结交新朋友。你将会遇到开发者和设计师、初学者和专家、研究人员和艺术家、教师和学生等各行各业的人士。我们的背景可能非常不同,但
React 让我们所有人都能够共同创建用户界面。
</Para>
</Center>
</div>
</div>
<div className="mt-20 px-5 lg:px-0 mb-6 max-w-4xl text-center text-opacity-80">
<div className="uwu-visible flex justify-center">
<img
alt="logo by @sawaratsuki1004"
title="logo by @sawaratsuki1004"
className="uwu-visible mb-10 lg:mb-8 h-24 lg:h-32"
src="/images/uwu.png"
/>
</div>
<Logo className="uwu-hidden text-brand dark:text-brand-dark w-24 lg:w-28 mb-10 lg:mb-8 mt-12 h-auto mx-auto self-start" />
<Header>
欢迎来到 <br className="hidden lg:inline" />
React 社区
</Header>
<ButtonLink
href={'/learn'}
type="primary"
size="lg"
label="Take the Tutorial">
开始 React 之旅
</ButtonLink>
</div>
</Section>
</div>
</>
);
}
function CTA({children, icon, href}) {
let Tag;
let extraProps;
if (href.startsWith('https://')) {
Tag = ExternalLink;
} else {
Tag = NextLink;
extraProps = {legacyBehavior: false};
}
return (
<Tag
{...extraProps}
href={href}
className="focus:outline-none focus-visible:outline focus-visible:outline-link focus:outline-offset-2 focus-visible:dark:focus:outline-link-dark group cursor-pointer w-auto justify-center inline-flex font-bold items-center mt-10 outline-none hover:bg-gray-40/5 active:bg-gray-40/10 hover:dark:bg-gray-60/5 active:dark:bg-gray-60/10 leading-tight hover:bg-opacity-80 text-lg py-2.5 rounded-full px-4 sm:px-6 ease-in-out shadow-secondary-button-stroke dark:shadow-secondary-button-stroke-dark text-primary dark:text-primary-dark">
{icon === 'native' && (
<svg
className="me-2.5 text-primary dark:text-primary-dark"
fill="none"
width="24"
height="24"
viewBox="0 0 72 72"
aria-hidden="true">
<g clipPath="url(#clip0_8_10998)">
<path
d="M54.0001 15H18.0001C16.3432 15 15.0001 16.3431 15.0001 18V42H33V48H12.9567L9.10021 57L24.0006 57C24.0006 55.3431 25.3437 54 27.0006 54H33V57.473C33 59.3786 33.3699 61.2582 34.0652 63H9.10021C4.79287 63 1.88869 58.596 3.5852 54.6368L9.0001 42V18C9.0001 13.0294 13.0295 9 18.0001 9H54.0001C58.9707 9 63.0001 13.0294 63.0001 18V25.4411C62.0602 25.0753 61.0589 24.8052 60.0021 24.6458C59.0567 24.5032 58.0429 24.3681 57.0001 24.2587V18C57.0001 16.3431 55.6569 15 54.0001 15Z"
fill="currentColor"
/>
<path
d="M48 42C48 40.3431 49.3431 39 51 39H54C55.6569 39 57 40.3431 57 42C57 43.6569 55.6569 45 54 45H51C49.3431 45 48 43.6569 48 42Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M45.8929 30.5787C41.8093 31.1947 39 34.8257 39 38.9556V57.473C39 61.6028 41.8093 65.2339 45.8929 65.8499C48.0416 66.174 50.3981 66.4286 52.5 66.4286C54.6019 66.4286 56.9584 66.174 59.1071 65.8499C63.1907 65.2339 66 61.6028 66 57.473V38.9556C66 34.8258 63.1907 31.1947 59.1071 30.5787C56.9584 30.2545 54.6019 30 52.5 30C50.3981 30 48.0416 30.2545 45.8929 30.5787ZM60 57.473V38.9556C60 37.4615 59.0438 36.637 58.2121 36.5116C56.2014 36.2082 54.1763 36 52.5 36C50.8237 36 48.7986 36.2082 46.7879 36.5116C45.9562 36.637 45 37.4615 45 38.9556V57.473C45 58.9671 45.9562 59.7916 46.7879 59.917C48.7986 60.2203 50.8237 60.4286 52.5 60.4286C54.1763 60.4286 56.2014 60.2203 58.2121 59.917C59.0438 59.7916 60 58.9671 60 57.473Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_8_10998">
<rect width="72" height="72" fill="white" />
</clipPath>
</defs>
</svg>
)}
{icon === 'framework' && (
<svg
className="me-2.5 text-primary dark:text-primary-dark"
fill="none"
width="24"
height="24"
viewBox="0 0 72 72"
aria-hidden="true">
<g clipPath="url(#clip0_10_21081)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M44.9136 29.0343C46.8321 26.9072 48 24.09 48 21C48 14.3726 42.6274 9 36 9C29.3726 9 24 14.3726 24 21C24 24.0904 25.1682 26.9079 27.0871 29.0351L21.0026 39.3787C20.0429 39.1315 19.0368 39 18 39C11.3726 39 6 44.3726 6 51C6 57.6274 11.3726 63 18 63C23.5915 63 28.2898 59.1757 29.6219 54H42.3781C43.7102 59.1757 48.4085 63 54 63C60.6274 63 66 57.6274 66 51C66 44.3726 60.6274 39 54 39C52.9614 39 51.9537 39.1319 50.9926 39.38L44.9136 29.0343ZM42 21C42 24.3137 39.3137 27 36 27C32.6863 27 30 24.3137 30 21C30 17.6863 32.6863 15 36 15C39.3137 15 42 17.6863 42 21ZM39.9033 32.3509C38.6796 32.7716 37.3665 33 36 33C34.6338 33 33.321 32.7717 32.0975 32.3512L26.2523 42.288C27.8635 43.8146 29.0514 45.7834 29.6219 48H42.3781C42.9482 45.785 44.1348 43.8175 45.7441 42.2913L39.9033 32.3509ZM54 57C50.6863 57 48 54.3137 48 51C48 47.6863 50.6863 45 54 45C57.3137 45 60 47.6863 60 51C60 54.3137 57.3137 57 54 57ZM24 51C24 47.6863 21.3137 45 18 45C14.6863 45 12 47.6863 12 51C12 54.3137 14.6863 57 18 57C21.3137 57 24 54.3137 24 51Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_10_21081">
<rect width="72" height="72" fill="white" />
</clipPath>
</defs>
</svg>
)}
{icon === 'code' && (
<svg
className="me-2.5 text-primary dark:text-primary-dark"
fill="none"
width="24"
height="24"
viewBox="0 0 72 72"
aria-hidden="true">
<g clipPath="url(#clip0_8_9064)">
<path
d="M44.7854 22.1142C45.4008 20.5759 44.6525 18.83 43.1142 18.2146C41.5758 17.5993 39.8299 18.3475 39.2146 19.8859L27.2146 49.8859C26.5992 51.4242 27.3475 53.1702 28.8858 53.7855C30.4242 54.4008 32.1701 53.6526 32.7854 52.1142L44.7854 22.1142Z"
fill="currentColor"
/>
<path
d="M9.87868 38.1214C8.70711 36.9498 8.70711 35.0503 9.87868 33.8787L18.8787 24.8787C20.0503 23.7072 21.9497 23.7072 23.1213 24.8787C24.2929 26.0503 24.2929 27.9498 23.1213 29.1214L16.2426 36.0001L23.1213 42.8787C24.2929 44.0503 24.2929 45.9498 23.1213 47.1214C21.9497 48.293 20.0503 48.293 18.8787 47.1214L9.87868 38.1214Z"
fill="currentColor"
/>
<path
d="M62.1213 33.8787L53.1213 24.8787C51.9497 23.7072 50.0503 23.7072 48.8787 24.8787C47.7071 26.0503 47.7071 27.9498 48.8787 29.1214L55.7574 36.0001L48.8787 42.8787C47.7071 44.0503 47.7071 45.9498 48.8787 47.1214C50.0503 48.293 51.9497 48.293 53.1213 47.1214L62.1213 38.1214C63.2929 36.9498 63.2929 35.0503 62.1213 33.8787Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_8_9064">
<rect width="72" height="72" fill="white" />
</clipPath>
</defs>
</svg>
)}
{icon === 'news' && (
<svg
className="me-2.5 text-primary dark:text-primary-dark"
fill="none"
width="24"
height="24"
viewBox="0 0 72 72"
aria-hidden="true">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12.7101 56.3758C13.0724 56.7251 13.6324 57 14.3887 57H57.6113C58.3676 57 58.9276 56.7251 59.2899 56.3758C59.6438 56.0346 59.8987 55.5407 59.9086 54.864C59.9354 53.022 59.9591 50.7633 59.9756 48H12.0244C12.0409 50.7633 12.0645 53.022 12.0914 54.864C12.1013 55.5407 12.3562 56.0346 12.7101 56.3758ZM12.0024 42H59.9976C59.9992 41.0437 60 40.0444 60 39C60 29.5762 59.9327 22.5857 59.8589 17.7547C59.8359 16.2516 58.6168 15 56.9938 15L15.0062 15C13.3832 15 12.1641 16.2516 12.1411 17.7547C12.0673 22.5857 12 29.5762 12 39C12 40.0444 12.0008 41.0437 12.0024 42ZM65.8582 17.6631C65.7843 12.8227 61.8348 9 56.9938 9H15.0062C10.1652 9 6.21572 12.8227 6.1418 17.6631C6.06753 22.5266 6 29.5477 6 39C6 46.2639 6.03988 51.3741 6.09205 54.9515C6.15893 59.537 9.80278 63 14.3887 63H57.6113C62.1972 63 65.8411 59.537 65.9079 54.9515C65.9601 51.3741 66 46.2639 66 39C66 29.5477 65.9325 22.5266 65.8582 17.6631ZM39 21C37.3431 21 36 22.3431 36 24C36 25.6569 37.3431 27 39 27H51C52.6569 27 54 25.6569 54 24C54 22.3431 52.6569 21 51 21H39ZM36 33C36 31.3431 37.3431 30 39 30H51C52.6569 30 54 31.3431 54 33C54 34.6569 52.6569 36 51 36H39C37.3431 36 36 34.6569 36 33ZM24 33C27.3137 33 30 30.3137 30 27C30 23.6863 27.3137 21 24 21C20.6863 21 18 23.6863 18 27C18 30.3137 20.6863 33 24 33Z"
fill="currentColor"
/>
</svg>
)}
{children}
<svg
className="text-primary dark:text-primary-dark rtl:rotate-180"
fill="none"
width="24"
height="24"
viewBox="0 0 72 72"
aria-hidden="true">
<path
className="transition-transform ease-in-out translate-x-[-8px] group-hover:translate-x-[8px]"
fillRule="evenodd"
clipRule="evenodd"
d="M40.0001 19.0245C41.0912 17.7776 42.9864 17.6513 44.2334 18.7423L58.9758 33.768C59.6268 34.3377 60.0002 35.1607 60.0002 36.0257C60.0002 36.8908 59.6268 37.7138 58.9758 38.2835L44.2335 53.3078C42.9865 54.3988 41.0913 54.2725 40.0002 53.0256C38.9092 51.7786 39.0355 49.8835 40.2824 48.7924L52.4445 36.0257L40.2823 23.2578C39.0354 22.1667 38.9091 20.2714 40.0001 19.0245Z"
fill="currentColor"
/>
<path
className="opacity-0 ease-in-out transition-opacity group-hover:opacity-100"
d="M60 36.0273C60 37.6842 58.6569 39.0273 57 39.0273H15C13.3431 39.0273 12 37.6842 12 36.0273C12 34.3704 13.3431 33.0273 15 33.0273H57C58.6569 33.0273 60 34.3704 60 36.0273Z"
fill="currentColor"
/>
</svg>
</Tag>
);
}
const reactConf2021Cover = '/images/home/conf2021/cover.svg';
const reactConf2019Cover = '/images/home/conf2019/cover.svg';
const communityImages = [
{
src: '/images/home/community/react_conf_fun.webp',
alt: 'People singing karaoke at React Conf',
},
{
src: '/images/home/community/react_india_sunil.webp',
alt: 'Sunil Pai speaking at React India',
},
{
src: '/images/home/community/react_conf_hallway.webp',
alt: 'A hallway conversation between two people at React Conf',
},
{
src: '/images/home/community/react_india_hallway.webp',
alt: 'A hallway conversation at React India',
},
{
src: '/images/home/community/react_conf_elizabet.webp',
alt: 'Elizabet Oliveira speaking at React Conf',
},
{
src: '/images/home/community/react_india_selfie.webp',
alt: 'People taking a group selfie at React India',
},
{
src: '/images/home/community/react_conf_nat.webp',
alt: 'Nat Alison speaking at React Conf',
},
{
src: '/images/home/community/react_india_team.webp',
alt: 'Organizers greeting attendees at React India',
},
];
function CommunityGallery() {
const ref = useRef();
const [shouldPlay, setShouldPlay] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
setShouldPlay(entry.isIntersecting);
});
},
{
root: null,
rootMargin: `${window.innerHeight}px 0px`,
}
);
observer.observe(ref.current);
return () => observer.disconnect();
}, []);
const [isLazy, setIsLazy] = useState(true);
// Either wait until we're scrolling close...
useEffect(() => {
if (!isLazy) {
return;
}
const rootVertical = parseInt(window.innerHeight * 2.5);
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setIsLazy(false);
}
});
},
{
root: null,
rootMargin: `${rootVertical}px 0px`,
}
);
observer.observe(ref.current);
return () => observer.disconnect();
}, [isLazy]);
// ... or until it's been a while after hydration.
useEffect(() => {
const timeout = setTimeout(() => {
setIsLazy(false);
}, 20 * 1000);
return () => clearTimeout(timeout);
}, []);
return (
<div
ref={ref}
className="relative flex overflow-x-hidden overflow-y-visible w-auto">
<div
className="w-full py-12 lg:py-20 whitespace-nowrap flex flex-row animate-marquee lg:animate-large-marquee"
style={{
animationPlayState: shouldPlay ? 'running' : 'paused',
}}>
<CommunityImages isLazy={isLazy} />
</div>
<div
aria-hidden="true"
className="w-full absolute top-0 py-12 lg:py-20 whitespace-nowrap flex flex-row animate-marquee2 lg:animate-large-marquee2"
style={{
animationPlayState: shouldPlay ? 'running' : 'paused',
}}>
<CommunityImages isLazy={isLazy} />
</div>
</div>
);
}
const CommunityImages = memo(function CommunityImages({isLazy}) {
return (
<>
{communityImages.map(({src, alt}, i) => (
<div
key={i}
className={cn(
`group flex justify-center px-5 min-w-[50%] lg:min-w-[25%] rounded-2xl relative`
)}>
<div
className={cn(
'h-auto relative rounded-2xl overflow-hidden before:-skew-x-12 before:absolute before:inset-0 before:-translate-x-full group-hover:before:animate-[shimmer_1s_forwards] before:bg-gradient-to-r before:from-transparent before:via-white/10 before:to-transparent transition-all ease-in-out duration-300',
i % 2 === 0
? 'rotate-2 group-hover:rotate-[-1deg] group-hover:scale-110 group-hover:shadow-lg lg:group-hover:shadow-2xl'
: 'group-hover:rotate-1 group-hover:scale-110 group-hover:shadow-lg lg:group-hover:shadow-2xl rotate-[-2deg]'
)}>
<img
loading={isLazy ? 'lazy' : 'eager'}
src={src}
alt={alt}
className="aspect-[4/3] h-full w-full flex object-cover rounded-2xl bg-gray-10 dark:bg-gray-80"
/>
</div>
</div>
))}
</>
);
});
function ExampleLayout({
filename,
left,
right,
activeArea,
hoverTopOffset = 0,
}) {
const contentRef = useRef(null);
useNestedScrollLock(contentRef);
const [overlayStyles, setOverlayStyles] = useState([]);
useEffect(() => {
if (activeArea) {
const nodes = contentRef.current.querySelectorAll(
'[data-hover="' + activeArea.name + '"]'
);
const nextOverlayStyles = Array.from(nodes)
.map((node) => {
const parentRect = contentRef.current.getBoundingClientRect();
const nodeRect = node.getBoundingClientRect();
let top = Math.round(nodeRect.top - parentRect.top) - 8;
let bottom = Math.round(nodeRect.bottom - parentRect.top) + 8;
let left = Math.round(nodeRect.left - parentRect.left) - 8;
let right = Math.round(nodeRect.right - parentRect.left) + 8;
top = Math.max(top, hoverTopOffset);
bottom = Math.min(bottom, parentRect.height - 12);
if (top >= bottom) {
return null;
}
return {
width: right - left + 'px',
height: bottom - top + 'px',
transform: `translate(${left}px, ${top}px)`,
};
})
.filter((s) => s !== null);
setOverlayStyles(nextOverlayStyles);
}
}, [activeArea, hoverTopOffset]);
return (
<div className="lg:ps-10 lg:pe-5 w-full">
<div className="mt-12 mb-2 lg:my-16 max-w-7xl mx-auto flex flex-col w-full lg:rounded-2xl lg:bg-card lg:dark:bg-card-dark">
<div className="flex-col gap-0 lg:gap-5 lg:rounded-2xl lg:bg-gray-10 lg:dark:bg-gray-70 shadow-inner-border dark:shadow-inner-border-dark lg:flex-row flex grow w-full mx-auto items-center bg-cover bg-center lg:bg-right ltr:lg:bg-[length:60%_100%] bg-no-repeat bg-meta-gradient dark:bg-meta-gradient-dark">
<div className="lg:-m-5 h-full shadow-nav dark:shadow-nav-dark lg:rounded-2xl bg-wash dark:bg-gray-95 w-full flex grow flex-col">
<div className="w-full bg-card dark:bg-wash-dark lg:rounded-t-2xl border-b border-black/5 dark:border-white/5">
<h3 className="text-sm my-1 mx-5 text-tertiary dark:text-tertiary-dark select-none text-start">
{filename}
</h3>
</div>
{left}
</div>
<div
ref={contentRef}
className="relative mt-0 lg:-my-20 w-full p-2.5 xs:p-5 lg:p-10 flex grow justify-center">
{right}
<div
className={cn(
'absolute z-10 inset-0 pointer-events-none transition-opacity transform-gpu',
activeArea ? 'opacity-100' : 'opacity-0'
)}>
{overlayStyles.map((styles, i) => (
<div
key={i}
className="top-0 start-0 bg-blue-30/5 border-2 border-link dark:border-link-dark absolute rounded-lg"
style={styles}
/>
))}
</div>
</div>
</div>
</div>
</div>
);
}
function useCodeHover(areas) {
const [hoverLine, setHoverLine] = useState(null);
const area = areas.get(hoverLine);
let meta;
if (area) {
const highlightLines = area.lines ?? [hoverLine];
meta = '```js {' + highlightLines.map((l) => l + 1).join(',') + '}';
}
return [area, meta, setHoverLine];
}
const example1Areas = new Map([
[2, {name: 'Video'}],
[3, {name: 'Thumbnail'}],
[4, {name: 'a'}],
[5, {name: 'h3'}],
[6, {name: 'p'}],
[7, {name: 'a'}],
[8, {name: 'LikeButton'}],
[9, {name: 'Video'}],
]);
function Example1() {
const [area, meta, onLineHover] = useCodeHover(example1Areas);
return (
<ExampleLayout
filename="Video.js"
activeArea={area}
left={
<CodeBlock
onLineHover={onLineHover}
isFromPackageImport={false}
noShadow={true}
noMargin={true}>
<div meta={meta}>{`function Video({ video }) {
return (
<div>
<Thumbnail video={video} />
<a href={video.url}>
<h3>{video.title}</h3>
<p>{video.description}</p>
</a>
<LikeButton video={video} />
</div>
);
}
`}</div>
</CodeBlock>
}
right={
<ExamplePanel height="113px">
<Video
video={{
id: 'ex1-0',
title: 'My video',
description: 'Video description',
image: 'blue',
url: null,
}}
/>
</ExamplePanel>
}
/>
);
}
const example2Areas = new Map([
[8, {name: 'VideoList'}],
[9, {name: 'h2'}],
[11, {name: 'Video', lines: [11]}],
[13, {name: 'VideoList'}],
]);
function Example2() {
const [area, meta, onLineHover] = useCodeHover(example2Areas);
const videos = [
{
id: 'ex2-0',
title: 'First video',
description: 'Video description',
image: 'blue',
},
{
id: 'ex2-1',
title: 'Second video',
description: 'Video description',
image: 'red',
},
{
id: 'ex2-2',
title: 'Third video',
description: 'Video description',
image: 'green',
},
];
return (
<ExampleLayout
filename="VideoList.js"
activeArea={area}
left={
<CodeBlock
onLineHover={onLineHover}
isFromPackageImport={false}
noShadow={true}
noMargin={true}>
<div meta={meta}>{`function VideoList({ videos, emptyHeading }) {
const count = videos.length;
let heading = emptyHeading;
if (count > 0) {
const noun = count > 1 ? 'Videos' : 'Video';
heading = count + ' ' + noun;
}
return (
<section>
<h2>{heading}</h2>
{videos.map(video =>
<Video key={video.id} video={video} />
)}
</section>
);
}`}</div>
</CodeBlock>
}
right={
<ExamplePanel height="22rem" noShadow={false} noPadding={true}>
<div className="m-4">
<VideoList videos={videos} />
</div>
</ExamplePanel>
}
/>
);
}
const example3Areas = new Map([