@modern-js/main-doc 2.0.0-canary.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/en/docusaurus-plugin-content-docs/current/apis/app/runtime/_category_.json +1 -1
  3. package/en/docusaurus-plugin-content-docs/current/apis/app/runtime/core/use-module-apps.md +62 -31
  4. package/en/docusaurus-plugin-content-docs/current/apis/app/runtime/router/router.md +174 -375
  5. package/en/docusaurus-plugin-content-docs/current/components/enable-bff.md +36 -0
  6. package/en/docusaurus-plugin-content-docs/current/components/enable-micro-frontend.md +13 -0
  7. package/en/docusaurus-plugin-content-docs/current/components/micro-master-manifest-config.md +15 -0
  8. package/en/docusaurus-plugin-content-docs/current/components/micro-runtime-config.md +18 -0
  9. package/en/docusaurus-plugin-content-docs/current/components/router-legacy-tip.md +1 -0
  10. package/en/docusaurus-plugin-content-docs/current/configure/app/auto-load-plugin.md +62 -0
  11. package/en/docusaurus-plugin-content-docs/current/configure/app/deploy/microFrontend.md +54 -0
  12. package/en/docusaurus-plugin-content-docs/current/configure/app/output/ssg.md +226 -0
  13. package/en/docusaurus-plugin-content-docs/current/configure/app/runtime/master-app.md +20 -39
  14. package/en/docusaurus-plugin-content-docs/current/configure/app/runtime/router.md +17 -4
  15. package/en/docusaurus-plugin-content-docs/current/configure/app/runtime/state.md +17 -4
  16. package/en/docusaurus-plugin-content-docs/current/configure/app/server/enable-framework-ext.md +47 -0
  17. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/frameworks.md +2 -0
  18. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/bff/function.md +10 -6
  19. package/en/docusaurus-plugin-content-docs/current/guides/advanced-features/ssg.md +6 -2
  20. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/css/_category_.json +4 -0
  21. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/data-fetch.md +1 -1
  22. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/routes.md +0 -2
  23. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/_category_.json +5 -0
  24. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/framework-plugin/extend.md +162 -0
  25. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/framework-plugin/hook-list.md +803 -0
  26. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/framework-plugin/hook.md +169 -0
  27. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/framework-plugin/implement.md +247 -0
  28. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/framework-plugin/introduction.md +49 -0
  29. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/framework-plugin/plugin-api.md +116 -0
  30. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/framework-plugin/relationship.md +118 -0
  31. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/generator/config/common.md +1 -1
  32. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/generator/config/module.md +3 -1
  33. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/generator/config/mwa.md +1 -9
  34. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/generator/project.md +2 -2
  35. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/micro-frontend/_category_.json +4 -0
  36. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/micro-frontend/c01-introduction.md +29 -0
  37. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/micro-frontend/c02-development.md +191 -0
  38. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/micro-frontend/c03-main-app.md +246 -0
  39. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/micro-frontend/c04-communicate.md +54 -0
  40. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/micro-frontend/c05-mixed-stack.md +24 -0
  41. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/_category_.json +4 -0
  42. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/auto-actions.md +90 -0
  43. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/computed-state.md +151 -0
  44. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/define-model.md +66 -0
  45. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/faq.md +43 -0
  46. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/manage-effects.md +259 -0
  47. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/model-communicate.md +219 -0
  48. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/performance.md +173 -0
  49. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/quick-start.md +116 -0
  50. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/redux-integration.md +21 -0
  51. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/test-model.md +43 -0
  52. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/typescript-best-practice.md +71 -0
  53. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/use-model.md +244 -0
  54. package/en/docusaurus-plugin-content-docs/current/guides/topic-detail/model/use-out-of-modernjs.md +51 -0
  55. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/_category_.json +5 -0
  56. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/c01-start.md +99 -0
  57. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/c02-component.md +56 -0
  58. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/c03-css.md +324 -0
  59. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/c04-routes.md +169 -0
  60. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/c05-loader.md +82 -0
  61. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/c06-model.md +260 -0
  62. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/c07-container.md +283 -0
  63. package/en/docusaurus-plugin-content-docs/current/tutorials/first-app/c08-entries.md +137 -0
  64. package/en/docusaurus-plugin-content-docs/current/tutorials/foundations/_category_.json +1 -1
  65. package/en/docusaurus-plugin-content-docs/current/tutorials/foundations/introduction.md +5 -3
  66. package/package.json +4 -4
  67. package/zh/apis/app/runtime/core/use-module-apps.md +2 -0
  68. package/zh/apis/app/runtime/router/router.md +169 -371
  69. package/zh/components/enable-bff.md +36 -0
  70. package/zh/components/micro-master-manifest-config.md +15 -0
  71. package/zh/components/router-legacy-tip.md +1 -0
  72. package/zh/configure/app/auto-load-plugin.md +62 -0
  73. package/zh/configure/app/deploy/microFrontend.md +0 -10
  74. package/zh/configure/app/output/ssg.md +1 -5
  75. package/zh/configure/app/runtime/master-app.md +4 -18
  76. package/zh/configure/app/runtime/router.md +19 -4
  77. package/zh/configure/app/runtime/state.md +7 -7
  78. package/zh/configure/app/server/enable-framework-ext.md +47 -0
  79. package/zh/configure/app/server/port.md +1 -1
  80. package/zh/configure/app/tools/_category_.json +1 -1
  81. package/zh/guides/advanced-features/bff/frameworks.md +2 -0
  82. package/zh/guides/advanced-features/bff/function.md +7 -5
  83. package/zh/guides/advanced-features/eslint.md +2 -1
  84. package/zh/guides/advanced-features/ssg.md +4 -0
  85. package/zh/guides/basic-features/data-fetch.md +1 -1
  86. package/zh/guides/basic-features/env-vars.md +1 -1
  87. package/zh/guides/basic-features/routes.md +0 -3
  88. package/zh/guides/topic-detail/generator/config/module.md +3 -1
  89. package/zh/guides/topic-detail/generator/config/mwa.md +1 -9
  90. package/zh/guides/topic-detail/model/quick-start.md +1 -1
  91. package/zh/tutorials/first-app/c06-model.md +5 -1
  92. package/zh/tutorials/first-app/c08-entries.md +1 -1
  93. package/zh/tutorials/foundations/introduction.md +5 -3
  94. package/en/docusaurus-plugin-content-docs/current/apis/app/overview.md +0 -12
  95. package/en/docusaurus-plugin-content-docs/current/configure/app/bff/fetcher.md +0 -28
  96. package/en/docusaurus-plugin-content-docs/current/configure/app/dev/with-master-app.md +0 -31
  97. package/en/docusaurus-plugin-content-docs/current/guides/overview.md +0 -11
  98. package/en/docusaurus-plugin-content-docs/current/tutorials/foundations/basic.md +0 -8
  99. package/zh/apis/app/overview.md +0 -11
  100. package/zh/apis/monorepo/overview.md +0 -11
  101. package/zh/configure/app/bff/fetcher.md +0 -31
  102. package/zh/configure/app/dev/with-master-app.md +0 -32
  103. package/zh/guides/overview.md +0 -11
  104. package/zh/tutorials/foundations/basic.md +0 -8
@@ -0,0 +1,324 @@
1
+ ---
2
+ title: 添加样式
3
+ ---
4
+
5
+ import Tabs from '@theme/Tabs';
6
+ import TabItem from '@theme/TabItem';
7
+
8
+ 上一章节中,我们学习了如何使用使用三方库中的组件。
9
+
10
+ 这一章节中,我们将学习如何实现 UI 组件。
11
+
12
+ ## 使用 CSS 写 JS 组件
13
+
14
+ 首先我们希望自己控制联系人头像的展示,实现这种设计稿:
15
+
16
+ ![design](https://lf3-static.bytednsdoc.com/obj/eden-cn/nuvjhpqnuvr/modern-website/tutorials/c03-css-expect.jpg)
17
+
18
+ 假设没有现成的组件可以实现,那就需要自己写些 CSS 了,这里我们使用 [styled-components](https://styled-components.com/),来实现类似的需求。Modern.js 开箱即用的支持 styled-components,既不需要安装依赖,也不需要做任何配置。
19
+
20
+ styled-components 通过模块化的方式,避免了传统 CSS 写法上的诸多问题。例如直接在元素的 style 属性上写样式,UI 视觉上的细节也会跟 UI 结构上的细节和业务逻辑混在一起。或是 classname 需要避免全局空间重名,需要用到命名规范的问题。
21
+
22
+ 在 `src/routes/page.tsx` 里修改顶部的代码:
23
+
24
+ ```js
25
+ import styled from '@modern-js/runtime/styled';
26
+ ```
27
+
28
+ 添加以下代码:
29
+
30
+ ```js
31
+ const Avatar = styled.img`
32
+ width: 50px;
33
+ height: 50px;
34
+ border: 4px solid #0ef;
35
+ border-radius: 50%;
36
+ `;
37
+ ```
38
+
39
+ 修改 `List.Item.Meta` 的代码:
40
+
41
+ ```tsx
42
+ <List.Item.Meta
43
+ avatar={<Avatar src={avatar} />}
44
+ title={name}
45
+ description={email}
46
+ />
47
+ ```
48
+
49
+ 执行 `pnpm run dev`,可以看到预期的运行结果:
50
+
51
+ ![result](https://lf3-static.bytednsdoc.com/obj/eden-cn/nuvjhpqnuvr/modern-website/tutorials/c03-css-result1.png)
52
+
53
+ 接下来我们做一点重构,为了增强可读性,让代码更容易维护,可以把 Avatar 组件拆分出去。我们在终端执行以下命令,创建新的文件:
54
+
55
+ <Tabs>
56
+ <TabItem value="macOS" label="macOS" default>
57
+
58
+ ```bash
59
+ mkdir -p src/components/Avatar
60
+ touch src/components/Avatar/index.tsx
61
+ ```
62
+
63
+ </TabItem>
64
+ <TabItem value="Windows" label="Windows">
65
+
66
+ ```powershell
67
+ mkdir -p src/components/Avatar
68
+ ni src/components/Avatar/index.tsx
69
+ ```
70
+
71
+ </TabItem>
72
+ </Tabs>
73
+
74
+ 把 `src/routes/page.tsx` 里的 `<Avatar>` 实现删掉,修改为:
75
+
76
+ ```ts
77
+ import Avatar from '../components/Avatar';
78
+ ```
79
+
80
+ `src/components/Avatar/index.tsx` 的内容,修改为:
81
+
82
+ ```ts
83
+ import styled from '@modern-js/runtime/styled';
84
+
85
+ const Avatar = styled.img`
86
+ width: 50px;
87
+ height: 50px;
88
+ border: 4px solid #0ef;
89
+ border-radius: 50%;
90
+ `;
91
+
92
+ export default Avatar;
93
+ ```
94
+
95
+ 执行 `pnpm run dev`,运行结果应该是一样的。
96
+
97
+ :::info 注
98
+ 采用目录形式 `Avatar/index.tsx` 而不是单文件形式 `Avatar.tsx` 的原因是,之后可以方便的在目录内部增加子文件,包括专用的资源(图片等)、专用子组件、CSS 文件等。
99
+ :::
100
+
101
+
102
+ ## 使用 Utility
103
+
104
+ 我们已经使用 style-components 实现 `<Avatar>` 组件,但当前的 UI 仍然不能让人满意,缺乏专业感,例如列表项内部的布局有点粗糙,很多地方没对齐。
105
+
106
+ 现在,我们自己来实现一个更好的 `Item` 组件,实现这样的设计稿:
107
+
108
+ ![design](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/06/design2.png)
109
+
110
+ 这次要实现的 UI 更复杂,有内部结构,但另一方面,并没有 `<Avatar>` 组件的**很粗的亮蓝色边框**这样很特殊的 UI,都是很常规的水平垂直布局、居中、字体样式等。这种情况下,其实根本没必要写 CSS,有更高效的、跟 styled-components 互补的实现方式:**Utility Class**。
111
+
112
+ Modern.js 集成了主流、轻量、通用的 Utility Class 工具库 [Tailwind CSS](https://tailwindcss.com/)。
113
+
114
+ 执行 `pnpm run new`,进行如下选择,开启 Tailwind CSS:
115
+
116
+ ```bash
117
+ ? 请选择你想要的操作 启用可选功能
118
+ ? 启用可选功能 启用 Tailwind CSS 支持
119
+ ```
120
+
121
+ 在 `modern.config.ts` 中注册 Tailwind 插件:
122
+
123
+ ```ts title="modern.config.ts"
124
+ import AppToolsPlugin, { defineConfig } from '@modern-js/app-tools';
125
+ import TailwindCSSPlugin from '@modern-js/plugin-tailwindcss';
126
+
127
+ // https://modernjs.dev/docs/apis/app/config
128
+ export default defineConfig({
129
+ runtime: {
130
+ router: true,
131
+ state: true,
132
+ },
133
+ server: {
134
+ ssr: true,
135
+ },
136
+ plugins: [AppToolsPlugin(), TailwindCSSPlugin()],
137
+ });
138
+ ```
139
+
140
+ 在 `src/routes/page.tsx` 顶部引入 Tailwind CSS 的 css 文件,就可以开始快速实现专业的 UI:
141
+
142
+ ```js
143
+ import 'tailwindcss/base.css';
144
+ import 'tailwindcss/components.css';
145
+ import 'tailwindcss/utilities.css';
146
+ ```
147
+
148
+ 先创建 Item 组件:
149
+
150
+ <Tabs>
151
+ <TabItem value="macOS" label="macOS" default>
152
+
153
+ ```bash
154
+ mkdir -p src/components/Item
155
+ touch src/components/Item/index.tsx
156
+ ```
157
+
158
+ </TabItem>
159
+ <TabItem value="Windows" label="Windows">
160
+
161
+ ```powershell
162
+ mkdir -p src/components/Item
163
+ ni src/components/Item/index.tsx
164
+ ```
165
+
166
+ </TabItem>
167
+ </Tabs>
168
+
169
+ 修改 `src/routes/page.tsx`,把 `List` 的 `render` 实现交给 `Item` 组件:
170
+
171
+ ```js
172
+ import { List } from 'antd';
173
+ import 'tailwindcss/base.css';
174
+ import 'tailwindcss/components.css';
175
+ import 'tailwindcss/utilities.css';
176
+ import Item from '../components/Item';
177
+
178
+ const getAvatar = (users: Array<{ name: string; email: string }>) =>
179
+ users.map(user => ({
180
+ ...user,
181
+ avatar: `https://avatars.dicebear.com/v2/identicon/${user.name}.svg`,
182
+ }));
183
+
184
+ const mockData = getAvatar([
185
+ { name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
186
+ { name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
187
+ { name: 'Bradley', email: 'd.wfovsqyo@gpkcjwjgb.fr' },
188
+ { name: 'Davis', email: '"t.kqkoj@utlkwnpwk.nu' },
189
+ ]);
190
+
191
+ function Index() {
192
+ return (
193
+ <div className="container lg mx-auto">
194
+ <List
195
+ dataSource={mockData}
196
+ renderItem={info => <Item key={info.name} info={info} />}
197
+ />
198
+ </div>
199
+ );
200
+ }
201
+
202
+ export default Index;
203
+ ```
204
+
205
+ 在父容器的上使用了 [Utility Class](https://tailwindcss.com/docs/container) ,快速实现了最基本的最大宽度、居中等样式。
206
+
207
+ 接下来实现 `src/components/Item/index.tsx`:
208
+
209
+ ```tsx
210
+ import Avatar from '../Avatar';
211
+
212
+ type InfoProps = {
213
+ avatar: string;
214
+ name: string;
215
+ email: string;
216
+ archived?: boolean;
217
+ };
218
+
219
+ const Item = ({ info }: { info: InfoProps }) => {
220
+ const { avatar, name, email, archived } = info;
221
+ return (
222
+ <div className="flex p-4 items-center border-gray-200 border-b">
223
+ <Avatar src={avatar} />
224
+ <div className="ml-4 flex-1 flex justify-between">
225
+ <div className="flex-1">
226
+ <p>{name}</p>
227
+ <p>{email}</p>
228
+ </div>
229
+ <button
230
+ type="button"
231
+ disabled={archived}
232
+ className={`bg-blue-500 text-white font-bold
233
+ py-2 px-4 rounded-full hover:bg-blue-700`}
234
+ >
235
+ Archive
236
+ </button>
237
+ </div>
238
+ </div>
239
+ );
240
+ };
241
+
242
+ export default Item;
243
+ ```
244
+
245
+ 执行 `pnpm run dev`,可以看到预期的运行结果:
246
+
247
+ ![result](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/06/result2.png)
248
+
249
+ 我们只使用了少量 Utility Class,比如 [Flex](https://tailwindcss.com/docs/display/)、[Padding](https://tailwindcss.com/docs/padding/)、[Margin](https://tailwindcss.com/docs/margin/)、[Text](https://tailwindcss.com/docs/text-color/)、[Font](https://tailwindcss.com/docs/font-weight/)、[Border](https://tailwindcss.com/docs/border-width),不写一行 CSS 就实现了符合设计稿的专业 UI。
250
+
251
+
252
+ ## 自定义 Utility Class
253
+
254
+ 我们也可以自己实现新的 Utility Class,方便在代码间复用。
255
+
256
+ Utility Class 本身也是一种**面向组件**的技术(将不同 class 用在一个组件上,等价于给这个组件设置了一些来自基类的属性),但 Utility Class 的 classname 是全局的(因为要用在任意组件/元素上),很适合用独立 CSS 文件来实现。
257
+
258
+ 创建一个新的 CSS 文件:
259
+
260
+ <Tabs>
261
+ <TabItem value="macOS" label="macOS" default>
262
+
263
+ ```bash
264
+ mkdir -p src/styles
265
+ touch src/styles/utils.css
266
+ ```
267
+
268
+ </TabItem>
269
+ <TabItem value="Windows" label="Windows">
270
+
271
+ ```powershell
272
+ mkdir -p src/styles
273
+ ni src/styles/utils.css
274
+ ```
275
+
276
+ </TabItem>
277
+ </Tabs>
278
+
279
+ 在 `src/routes/page.tsx` 里导入 `utils.css`:
280
+
281
+ ```js
282
+ import '../styles/utils.css';
283
+ ```
284
+
285
+ 在 `src/routes/styles/utils.css` 里实现一个名为 `custom-text-gray` 的 Utility Class。
286
+
287
+ ```css
288
+ :root {
289
+ --custom-text-color:rgb(113, 128, 150);
290
+ }
291
+
292
+ .custom-text-gray {
293
+ color: var(--custom-text-color);
294
+ }
295
+ ```
296
+
297
+ :::info 注
298
+ Modern.js 集成了 [PostCSS](/docs/guides/basic-features/css/postcss),支持现代 CSS 语法特性,比如 [custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*)。
299
+ :::
300
+
301
+ 在 `src/routes/components/Item/index.tsx` 里使用,把:
302
+
303
+ ```js
304
+ <div className="ml-4 flex-1 flex justify-between">
305
+ ```
306
+
307
+ 改成:
308
+
309
+ ```js
310
+ <div className="ml-4 custom-text-gray flex-1 flex justify-between">
311
+ ```
312
+
313
+ 执行 `pnpm run dev`,可以看到字体颜色改变了:
314
+
315
+ ![design2](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/06/result3.png)
316
+
317
+ :::info 注
318
+ 此处只是为了演示 Utility Class 用法。真实项目中,在有 Tailwind CSS 的情况下,这种 Utility Class 没什么价值,应该通过配置 Design System 的 [**theme**](https://tailwindcss.com/docs/customizing-colors) 来增加字体颜色。
319
+
320
+ `utils.css` 也可以写成 `utils.scss` 或 `utils.less`,Modern.js 对 SCSS 和 Less 同样提供开箱即用的支持。
321
+
322
+ 不过在 PostCSS 的支持下,现代 CSS 应该足以满足这些开发需求,性能相较于预处理器也更好,建议优先用 .css 文件。
323
+ :::
324
+
@@ -0,0 +1,169 @@
1
+ ---
2
+ title: 添加客户端路由
3
+ ---
4
+
5
+ import Tabs from '@theme/Tabs';
6
+ import TabItem from '@theme/TabItem';
7
+
8
+ 上一章节中,我们学习了如何为创建 UI 组件,并添加样式。
9
+
10
+ 这一章节中,我们将会学习如何添加**客户端路由**。
11
+
12
+ 之前我们已经为联系人列表增加了 Archive 按钮,接下来我们添加一个客户端路由 `/archives`,访问这个路由时,只显示已存档的联系人,而原有的 `/` 继续显示所有联系人。
13
+
14
+ 新建 `src/routes/archives/page.tsx` 文件:
15
+
16
+ <Tabs>
17
+ <TabItem value="macOS" label="macOS" default>
18
+
19
+ ```bash
20
+ mkdir -p src/routes/archives
21
+ touch src/routes/archives/page.tsx
22
+ ```
23
+
24
+ </TabItem>
25
+ <TabItem value="Windows" label="Windows">
26
+
27
+ ```powershell
28
+ mkdir -p src/routes/archives
29
+ ni src/routes/archives/page.tsx
30
+ ```
31
+
32
+ </TabItem>
33
+ </Tabs>
34
+
35
+ 添加如下代码:
36
+
37
+ ```tsx title="src/archives/page.tsx"
38
+ import { List } from 'antd';
39
+ import { Helmet } from '@modern-js/runtime/head';
40
+ import Item from '../../components/Item';
41
+
42
+ const getAvatar = (users: Array<{ name: string; email: string }>) =>
43
+ users.map(user => ({
44
+ ...user,
45
+ avatar: `https://avatars.dicebear.com/v2/identicon/${user.name}.svg`,
46
+ }));
47
+
48
+ const getMockArchivedData = () =>
49
+ getAvatar([
50
+ { name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
51
+ { name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
52
+ ]);
53
+ function Index() {
54
+ return (
55
+ <div className="container lg mx-auto">
56
+ <Helmet>
57
+ <title>Archives</title>
58
+ </Helmet>
59
+ <List
60
+ dataSource={getMockArchivedData()}
61
+ renderItem={info => <Item key={info.name} info={info} />}
62
+ />
63
+ </div>
64
+ );
65
+ }
66
+
67
+ export default Index;
68
+ ```
69
+
70
+ 这里使用了 [React Helmet](https://github.com/nfl/react-helmet) 的 `Helmet` 组件,在 `src/routes/page.tsx` 中也添加 Helmet 组件:
71
+
72
+ ```tsx
73
+ import { Helmet } from '@modern-js/runtime/head';
74
+
75
+ function Index() {
76
+ return (
77
+ <div className="container lg mx-auto">
78
+ <Helmet>
79
+ <title>All</title>
80
+ </Helmet>
81
+ ...
82
+ </div>
83
+ );
84
+ }
85
+ ```
86
+
87
+ :::info 注
88
+ Modern.js 默认集成了 react-helmet,也可以结合 SSR 使用,满足 SEO 需求。
89
+ :::
90
+
91
+ 因为现在有多个页面,都需要用到前面的 Utility Class,因此我们需要把样式文件移动到 `src/routes/layout.tsx`:
92
+
93
+ ```tsx
94
+ import 'tailwindcss/base.css';
95
+ import 'tailwindcss/components.css';
96
+ import 'tailwindcss/utilities.css';
97
+ import '../styles/utils.css';
98
+ ```
99
+
100
+ 执行 `pnpm run dev`,访问 `http://localhost:8080`,可以看到完整的联系人,页面的标题是 All:
101
+
102
+ ![display1](https://lf3-static.bytednsdoc.com/obj/eden-cn/nuvjhpqnuvr/modern-website/tutorials/c04-archives.png)
103
+
104
+ 访问 `http://localhost:8080/archives`,只会看到已存档的联系人,页面的标题是 Archives:
105
+
106
+ ![display](https://lf3-static.bytednsdoc.com/obj/eden-cn/nuvjhpqnuvr/modern-website/tutorials/c04-all.png)
107
+
108
+ 查看页面 HTML 源码,可以看到两个页面的内容是一样,是在客户端针对不同 URL 渲染不同内容。
109
+
110
+ **接下来我们增加一个简单的导航栏,让用户能在两个列表之间切换**。
111
+
112
+ 打开 `src/routes/layout.tsx`,在顶部导入 Radio 组件:
113
+
114
+ ```tsx
115
+ import { Radio } from 'antd';
116
+ ```
117
+
118
+ 然后将 UI 最顶部进行修改,增加一组单选框
119
+
120
+ ```tsx {4-9}
121
+ export default function Layout() {
122
+ return (
123
+ <div>
124
+ <div className="h-16 p-2 flex items-center justify-center">
125
+ <Radio.Group onChange={handleSetList} value={currentList}>
126
+ <Radio value="/">All</Radio>
127
+ <Radio value="/archives">Archives</Radio>
128
+ </Radio.Group>
129
+ </div>
130
+ <Outlet />
131
+ </div>
132
+ );
133
+ }
134
+ ```
135
+
136
+ 然后我们来实现 `currentList` 和 `handleSetList`。
137
+
138
+ 引入三个 React Hook:`useState` 和 `useNavigate` 和 `useParams`,以及 Ant Design 的事件类型定义:
139
+
140
+ ```js
141
+ import { useState } from 'react';
142
+ import { Radio, RadioChangeEvent } from 'antd';
143
+ import { Outlet, useLocation, useNavigate } from "@modern-js/runtime/router";
144
+ ```
145
+
146
+ 最后在 Layout 组件里增加局部状态和相关逻辑:
147
+
148
+ ```tsx {2-9}
149
+ export default function Layout() {
150
+ const navigate = useNavigate();
151
+ const location = useLocation();
152
+ const [currentList, setList] = useState(location.pathname || '/');
153
+ const handleSetList = (e: RadioChangeEvent) => {
154
+ const { value } = e.target;
155
+ setList(value);
156
+ navigate(value);
157
+ };
158
+ return (
159
+ ...
160
+ }
161
+ ```
162
+
163
+ 到这里就已经完成了页面导航栏实现,执行 `pnpm run dev` 查看效果:
164
+
165
+ ![display2](https://lf3-static.bytednsdoc.com/obj/eden-cn/nuvjhpqnuvr/modern-website/tutorials/c04-switch.png)
166
+
167
+ 点击导航栏中 Archives,可以看到单选框的选中状态和 URL 都会变化,页面没有刷新,只发生了 CSR。
168
+
169
+ 通过 URL 访问两个页面,可以看到 HTML 内容是不同的,这是因为在 SSR 阶段页面就执行了客户端路由的逻辑,HTML 里已经包含了最终的渲染结果。
@@ -0,0 +1,82 @@
1
+ ---
2
+ title: 添加 Loader
3
+ ---
4
+
5
+ 上一章节中,我们学习了如何添加客户端路由。
6
+
7
+ 这一章节中,我们将会学习如何为**路由组件添加 Loader**。
8
+
9
+ 到目前为止,我们都是通过硬编码的方式,为组件提供数据。如果要从远端获取数据,通常情况下会使用 `useEffect` 来做。但在启用 SSR 的情况下,`useEffect` 是不会在服务端执行的,所以这种 SSR 只能渲染很有限的 UI。
10
+
11
+ Modern.js 为提供了 Data Loader 的能力,支持同构的在组件中获取数据,让 SSR 的价值最大化。
12
+
13
+ 下面我们演示如何为路由组件添加 Data Loader,并模拟远端数据获取。我们使用 faker 来 mock 需要的数据,首先安装依赖:
14
+
15
+ ```bash
16
+ pnpm add faker@5
17
+ pnpm add @types/faker@5 -D
18
+ ```
19
+
20
+ 修改 `src/routes/page.tsx`:
21
+
22
+ ```tsx
23
+ import { name, internet } from 'faker';
24
+
25
+ type LoaderData = {
26
+ code: number;
27
+ data: {
28
+ name: string;
29
+ avatar: string;
30
+ email: string;
31
+ }[];
32
+ };
33
+
34
+ export const loader = async (): Promise<LoaderData> => {
35
+ const data = new Array(20).fill(0).map(() => {
36
+ const firstName = name.firstName();
37
+ return {
38
+ name: firstName,
39
+ avatar: `https://avatars.dicebear.com/api/identicon/${firstName}.svg`,
40
+ email: internet.email(),
41
+ };
42
+ });
43
+
44
+ return {
45
+ code: 200,
46
+ data,
47
+ };
48
+ };
49
+ ```
50
+
51
+ :::note
52
+ Data Loader 并非只为 SSR 工作。在 CSR 项目中,Data Loader 也可以避免数据获取依赖 UI 渲染,解决请求瀑布流的问题。未来,Modern.js 也会为这一特性添加更多能力,例如预获取、数据缓存等。
53
+ :::
54
+
55
+ Modern.js 也提供了一个叫 `useLoaderData` 的 hooks API,我们修改 `src/routes/page.tsx` 导出的组件:
56
+
57
+ ```tsx {1,4,13}
58
+ import { useLoaderData } from '@modern-js/runtime/router';
59
+
60
+ function Index() {
61
+ const { data } = useLoaderData() as LoaderData;
62
+
63
+ return (
64
+ <div className="container lg mx-auto">
65
+ <Helmet>
66
+ <title>All</title>
67
+ </Helmet>
68
+ <List
69
+ dataSource={data}
70
+ renderItem={(info) => <Item key={info.name} info={info} />}
71
+ />
72
+ </div>
73
+ );
74
+ }
75
+
76
+ export default Index;
77
+ ```
78
+
79
+ <!-- Todo 重新截图,SSR 内容 -->
80
+ 重新执行 `pnpm run dev`,查看 `view-source:http://localhost:8080/`,或在 devtools 的 Network 面板里查看 HTML 请求的「 Preview 」,可以看到 SSR 渲染出来的 HTML 已经包含完整的 UI:
81
+
82
+ ![display6](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/11/display6.png)