@modern-js/main-doc 0.0.0-next-20221203140534 → 0.0.0-next-20221205074243

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/en/docusaurus-plugin-content-docs/current/apis/app/commands/new.md +0 -2
  3. package/en/docusaurus-plugin-content-docs/current/apis/app/runtime/core/bootstrap.md +17 -3
  4. package/en/docusaurus-plugin-content-docs/current/guides/basic-features/builder.md +46 -0
  5. package/en/docusaurus-plugin-content-docs/current.json +18 -18
  6. package/package.json +3 -3
  7. package/zh/apis/app/commands/new.md +0 -2
  8. package/zh/apis/app/runtime/app/_category_.json +1 -1
  9. package/zh/apis/app/runtime/bff/_category_.json +1 -1
  10. package/zh/apis/app/runtime/core/_category_.json +1 -1
  11. package/zh/apis/app/runtime/core/bootstrap.md +17 -3
  12. package/zh/apis/app/runtime/model/_category_.json +1 -1
  13. package/zh/apis/app/runtime/router/_category_.json +1 -1
  14. package/zh/apis/app/runtime/ssr/_category_.json +1 -1
  15. package/zh/apis/app/runtime/testing/_category_.json +1 -1
  16. package/zh/apis/app/runtime/utility/_category_.json +1 -1
  17. package/zh/apis/app/runtime/web-server/_category_.json +1 -1
  18. package/zh/configure/app/output/ssg.md +118 -114
  19. package/zh/configure/app/server/ssr.md +0 -2
  20. package/zh/guides/advanced-features/custom-app.md +8 -2
  21. package/zh/guides/advanced-features/ssg.md +74 -63
  22. package/zh/guides/advanced-features/ssr.md +76 -11
  23. package/zh/guides/basic-features/builder.md +46 -0
  24. package/zh/guides/basic-features/css/_category_.json +1 -1
  25. package/zh/guides/basic-features/css/less-sass.md +1 -14
  26. package/zh/guides/basic-features/data-fetch.md +1 -1
  27. package/zh/guides/basic-features/routes.md +32 -35
  28. package/zh/guides/concept/entries.md +4 -4
  29. package/zh/tutorials/first-app/c01-getting-started/1.2-minimal-mwa.md +2 -2
  30. package/zh/tutorials/first-app/c01-getting-started/1.4-enable-ssr.md +5 -2
  31. package/zh/tutorials/first-app/c02-generator-and-studio/2.2-boilerplates.md +4 -6
  32. package/zh/tutorials/first-app/c02-generator-and-studio/2.3-configuration.md +2 -4
  33. package/zh/tutorials/first-app/c03-ide/3.1-setting-up.md +1 -1
  34. package/zh/tutorials/first-app/c03-ide/3.2-hints-in-ide.md +44 -50
  35. package/zh/tutorials/first-app/c03-ide/3.3-autofix-in-ide.md +1 -1
  36. package/zh/tutorials/first-app/c03-ide/3.4-autofix-in-cli.md +4 -4
  37. package/zh/tutorials/first-app/c04-es6-plus-and-ts/4.1-use-es6-plus.md +8 -21
  38. package/zh/tutorials/first-app/c04-es6-plus-and-ts/4.2-use-typescript.md +37 -13
  39. package/zh/tutorials/first-app/c05-component/5.1-use-ui-library.md +3 -13
  40. package/zh/tutorials/first-app/c05-component/5.2-use-standalone-component.md +1 -21
  41. package/zh/tutorials/first-app/c06-css-and-component/6.1-css-in-js.md +9 -9
  42. package/zh/tutorials/first-app/c06-css-and-component/6.2-utility-class.md +9 -14
  43. package/zh/tutorials/first-app/c06-css-and-component/6.3-postcss.md +7 -7
  44. package/zh/tutorials/first-app/c06-css-and-component/6.4-design-system.md +1 -1
  45. package/zh/tutorials/first-app/c06-css-and-component/6.5-storybook.md +2 -2
  46. package/zh/tutorials/first-app/c06-css-and-component/6.6-testing.md +8 -17
  47. package/zh/tutorials/first-app/c07-app-entry/7.1-intro.md +23 -18
  48. package/zh/tutorials/first-app/c07-app-entry/7.2-add-entry-in-cli.md +30 -30
  49. package/zh/tutorials/first-app/c07-app-entry/7.3-manage-entries-by-hand.md +4 -9
  50. package/zh/tutorials/first-app/c08-client-side-routing/8.1-code-based-routing.md +66 -63
  51. package/zh/tutorials/first-app/c09-bff/9.2-enable-bff.md +35 -33
  52. package/zh/tutorials/first-app/c09-bff/9.3-fetch-bff.md +28 -102
  53. package/zh/tutorials/first-app/c10-model/10.1-application-architecture.md +4 -6
  54. package/zh/tutorials/first-app/c10-model/10.2-add-model.md +3 -3
  55. package/zh/tutorials/first-app/c10-model/10.3-use-model.md +21 -20
  56. package/zh/tutorials/first-app/c10-model/10.4-testing.md +2 -2
  57. package/zh/tutorials/first-app/c11-container/11.1-use-model-with-app-state.md +34 -68
  58. package/zh/tutorials/first-app/c11-container/11.2-add-container.md +40 -37
  59. package/zh/tutorials/first-app/c11-container/11.3-use-loader.md +6 -4
  60. package/zh/tutorials/first-app/c11-container/11.4-testing.md +2 -2
  61. package/zh/tutorials/foundations/introduction.md +1 -1
  62. package/en/docusaurus-plugin-content-docs/current/apis/app/runtime/default-alias.md +0 -25
  63. package/zh/apis/app/runtime/default-alias.md +0 -23
  64. package/zh/guides/basic-features/image.md +0 -43
  65. package/zh/guides/topic-detail/compile-speed.md +0 -182
  66. package/zh/guides/troubleshooting/compile.md +0 -379
  67. package/zh/tutorials/first-app/c08-client-side-routing/8.2-file-based-routing.md +0 -310
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: 使用自控式路由​​​​
2
+ title: 使用约定式路由​​​​
3
3
  ---
4
4
 
5
5
  上一章节中,我们学习了如何创建应用入口。
@@ -21,55 +21,53 @@ export default defineConfig({
21
21
 
22
22
  之前我们已经为联系人列表增加了 Archive 按钮,接下来我们添加一个客户端路由 `/archives`,访问这个路由时,只显示已存档的联系人,而原有的 `/` 继续显示所有联系人。
23
23
 
24
- 打开 `src/contacts/App.tsx`,在原有的 `mockData` 下方新增 `mockArchivedData`:
24
+ 新建 `src/contacts/routes/archives/page.tsx`, 添加如下代码:
25
25
 
26
- ```js
27
- const mockData = getAvatar([
28
- { name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
29
- { name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
30
- { name: 'Bradley', email: 'd.wfovsqyo@gpkcjwjgb.fr' },
31
- { name: 'Davis', email: '"t.kqkoj@utlkwnpwk.nu' },
32
- ]);
33
-
34
- const mockArchivedData = getAvatar([
35
- { name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
36
- { name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
37
- ]);
26
+ ```ts
27
+ import { List } from 'antd';
28
+ import { Helmet } from '@modern-js/runtime/head';
29
+ import Item from '../components/Item';
30
+
31
+ const getAvatar = (users: Array<{ name: string; email: string }>) =>
32
+ users.map(user => ({
33
+ ...user,
34
+ avatar: `https://avatars.dicebear.com/v2/identicon/${user.name}.svg`,
35
+ }));
36
+
37
+ const getMockArchivedData = () =>
38
+ getAvatar([
39
+ { name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
40
+ { name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
41
+ ]);
42
+ function Index() {
43
+ return (
44
+ <div className="container lg mx-auto">
45
+ <Helmet>
46
+ <title>Archives</title>
47
+ </Helmet>
48
+ <List
49
+ dataSource={getMockArchivedData()}
50
+ renderItem={info => <Item key={info.name} info={info} />}
51
+ />
52
+ </div>
53
+ );
54
+ }
55
+
56
+ export default Index;
38
57
  ```
39
58
 
40
- 在文件顶部引入 [React Router](https://reactrouter.com/) 的 `Route`、`Switch` 和 [React Helmet](https://github.com/nfl/react-helmet) 的 `Helmet` 组件:
59
+ 这里使用了 [React Helmet](https://github.com/nfl/react-helmet) 的 `Helmet` 组件,在 `src/contacts/routes/page.tsx` 中也添加 Helmet 组件:
41
60
 
42
- ```js
43
- import { Route, Switch } from '@modern-js/runtime/router';
61
+ ```tsx
44
62
  import { Helmet } from '@modern-js/runtime/head';
45
- ```
46
63
 
47
- 在 `App` 组件中使用 `Route` 写两个路由,分别用不同的 mock 数据渲染列表:
48
-
49
- ```js
50
64
  function App() {
51
65
  return (
52
66
  <div className="container lg mx-auto">
53
- <Switch>
54
- <Route path="/" exact={true}>
55
- <Helmet>
56
- <title>All</title>
57
- </Helmet>
58
- <List
59
- dataSource={mockData}
60
- renderItem={info => <Item key={info.name} info={info} />}
61
- />
62
- </Route>
63
- <Route path="/archives" exact={true}>
64
- <Helmet>
65
- <title>Archives</title>
66
- </Helmet>
67
- <List
68
- dataSource={mockArchivedData}
69
- renderItem={info => <Item key={info.name} info={info} />}
70
- />
71
- </Route>
72
- </Switch>
67
+ <Helmet>
68
+ <title>All</title>
69
+ </Helmet>
70
+ ...
73
71
  </div>
74
72
  );
75
73
  }
@@ -77,12 +75,8 @@ function App() {
77
75
 
78
76
  :::info 注
79
77
  Modern.js 默认集成了 react-helmet,无需安装依赖,可以直接使用,也可以结合 SSR 使用,满足 SEO 需求。
80
-
81
- Modern.js 也默认集成了 react-router,无需安装依赖和自己配置 `BrowserRouter` 等样板代码,可以直接用 Route、Switch 等组件实现路由逻辑。
82
78
  :::
83
79
 
84
- React Router v4+ 有两种用法,一种是 `component-based` 的,一种是基于全局配置的。这两种都由开发者自己用代码来控制客户端路由逻辑,所以我们把这种模式称作【**自控式路由**】。
85
-
86
80
  执行 `pnpm run dev`,访问 `http://localhost:8080/contacts`,可以看到完整的联系人,页面的标题是 All:
87
81
 
88
82
  ![display](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/08/display.png)
@@ -95,7 +89,7 @@ React Router v4+ 有两种用法,一种是 `component-based` 的,一种是
95
89
 
96
90
  **接下来我们增加一个简单的导航栏,让用户能在两个列表之间切换**。
97
91
 
98
- 打开 src/contacts/App.tsx,在顶部导入 Radio 组件:
92
+ 打开 `src/contacts/layout.tsx`,在顶部导入 Radio 组件:
99
93
 
100
94
  ```tsx
101
95
  import { List, Radio } from 'antd';
@@ -103,39 +97,48 @@ import { List, Radio } from 'antd';
103
97
 
104
98
  然后将 UI 最顶部进行修改,增加一组单选框
105
99
 
106
- ```tsx {3-8}
107
- return (
108
- <div className="container lg mx-auto">
109
- <div className="h-16 p-2 flex items-center justify-center">
110
- <Radio.Group onChange={handleSetList} value={currentList}>
111
- <Radio value="/">All</Radio>
112
- <Radio value="/archives">Archives</Radio>
113
- </Radio.Group>
100
+ ```tsx {5-8}
101
+ export default function Layout() {
102
+ return (
103
+ <div>
104
+ <div className="h-16 p-2 flex items-center justify-center">
105
+ <Radio.Group onChange={handleSetList} value={currentList}>
106
+ <Radio value="/">All</Radio>
107
+ <Radio value="/archives">Archives</Radio>
108
+ </Radio.Group>
109
+ </div>
110
+ <Outlet />
114
111
  </div>
115
- <Switch>
112
+ );
113
+ }
114
+
116
115
  ```
117
116
 
118
117
  然后我们来实现 `currentList` 和 `handleSetList`。
119
118
 
120
- 引入两个 React Hook:`useState` 和 `useHistory`,以及 Ant Design 的事件类型定义:
119
+ 引入三个 React Hook:`useState` 和 `useNavigate` 和 `useParams`,以及 Ant Design 的事件类型定义:
121
120
 
122
121
  ```js
123
122
  import { useState } from 'react';
124
- import { List, Radio, RadioChangeEvent } from 'antd';
125
- import { Route, Switch, useHistory } from '@modern-js/runtime/router';
123
+ import { Radio, RadioChangeEvent } from 'antd';
124
+ import { Outlet, useNavigate, useParams } from '@modern-js/runtime/router';
126
125
  ```
127
126
 
128
- 最后在 App 组件里增加局部状态和相关逻辑:
127
+ 最后在 Layout 组件里增加局部状态和相关逻辑:
129
128
 
130
129
  ```js {2-8}
131
- function App() {
132
- const history = useHistory();
133
- const [currentList, setList] = useState(history.location.pathname || '/');
130
+ export default function Layout() {
131
+ const navigate = useNavigate();
132
+ const params = useParams();
133
+ const [currentList, setList] = useState(params.pathname || '/');
134
134
  const handleSetList = (e: RadioChangeEvent) => {
135
135
  const { value } = e.target;
136
136
  setList(value);
137
- history.push(value);
137
+ navigate(value);
138
138
  };
139
+ return (
140
+ ...
141
+ }
139
142
  ```
140
143
 
141
144
  到这里就已经完成了页面导航栏实现,执行 `pnpm run dev` 查看效果:
@@ -18,8 +18,6 @@ title: 启用 BFF
18
18
  # 请选择运行时框
19
19
  ❯ Express
20
20
  Koa
21
- Egg
22
- Nest
23
21
  ```
24
22
 
25
23
  :::info 注
@@ -30,43 +28,47 @@ title: 启用 BFF
30
28
 
31
29
  ```md
32
30
  .
33
- ├── .vscode/
34
- ├── api/
35
- │ ├── info/
36
- │ │ └── [type].ts
37
- │ ├── .eslintrc.json
38
- │ ├── _app.ts
39
- │ └── index.ts
40
- ├── src/
41
- │ ├── contacts/
42
- │ │ ├── components/
43
- │ │ │ ├── Avatar/
44
- │ │ │ │ ├── index.stories.tsx
45
- │ │ │ │ └── index.tsx
46
- │ │ │ └── Item/
47
- │ │ │ ├── index.test.tsx
48
- │ │ │ └── index.tsx
49
- │ │ ├── styles/
50
- │ │ │ └── utils.css
51
- │ │ ├── App.css
52
- │ │ └── App.tsx
53
- │ ├── landing-page/
54
- │ │ └── pages/
55
- │ │ ├── comments/
56
- │ │ │ └── [commentTitle]/
57
- │ │ │ └── index.tsx
58
- │ │ ├── _app.tsx
59
- │ │ ├── docs.tsx
60
- │ │ └── index.tsx
61
- │ ├── .eslintrc.json
62
- │ └── modern-app-env.d.ts
63
- ├── .editorconfig
31
+ ├── .eslintrc.js
64
32
  ├── .gitignore
33
+ ├── .husky
65
34
  ├── .npmrc
66
35
  ├── .nvmrc
36
+ ├── .prettierrc
37
+ ├── .vscode
67
38
  ├── README.md
39
+ ├── api
40
+ │ ├── _app.ts
41
+ │ ├── index.ts
42
+ │ └── info
43
+ │ └── [type].ts
44
+ ├── modern.config.ts
68
45
  ├── package.json
69
46
  ├── pnpm-lock.yaml
47
+ ├── src
48
+ │ ├── .eslintrc.js
49
+ │ ├── contacts
50
+ │ │ ├── index.test.ts
51
+ │ │ └── routes
52
+ │ │ ├── archives
53
+ │ │ │ └── page.tsx
54
+ │ │ ├── components
55
+ │ │ │ ├── Avatar
56
+ │ │ │ │ ├── index.stories.tsx
57
+ │ │ │ │ └── index.tsx
58
+ │ │ │ └── Item
59
+ │ │ │ ├── index.test.tsx
60
+ │ │ │ └── index.tsx
61
+ │ │ ├── index.css
62
+ │ │ ├── layout.tsx
63
+ │ │ ├── page.tsx
64
+ │ │ └── styles
65
+ │ │ └── utils.css
66
+ │ ├── landing-page
67
+ │ │ └── routes
68
+ │ │ ├── index.css
69
+ │ │ ├── layout.tsx
70
+ │ │ └── page.tsx
71
+ │ └── modern-app-env.d.ts
70
72
  └── tsconfig.json
71
73
  ```
72
74
 
@@ -35,7 +35,7 @@ mv api/index.ts api/contacts.ts
35
35
  我们使用 [faker](https://github.com/Marak/Faker.js) 来 mock 需要的数据,首先安装依赖:
36
36
 
37
37
  ```bash
38
- pnpm add faker
38
+ pnpm add faker@5.5.3
39
39
  ```
40
40
 
41
41
  将 `api/contacts.ts` 内容改为:
@@ -65,69 +65,22 @@ export const get = async () => {
65
65
 
66
66
  ![browser-result](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/08/api-result.png)
67
67
 
68
- 接下来我们把 `src/contacts/App.tsx` 里硬编码的 `mockData` 改成从 BFF 加载。
68
+ 接下来我们把 `src/routes/contacts/page.tsx` 里硬编码的 `getMockData` 改成从 BFF 加载:
69
69
 
70
- 我们将业务逻辑迁移到组件中,`App.tsx` 只保留简单的路由部分,执行:
71
-
72
- <Tabs>
73
- <TabItem value="macOS" label="macOS" default>
74
-
75
- ```bash
76
- mkdir -p src/contacts/components/AllContacts/
77
- mkdir -p src/contacts/components/ArchivedContacts/
78
- touch src/contacts/components/AllContacts/index.tsx
79
- touch src/contacts/components/ArchivedContacts/index.tsx
80
- ```
81
-
82
- </TabItem>
83
- <TabItem value="Windows" label="Windows">
84
-
85
- ```powershell
86
- mkdir -p src/contacts/components/AllContacts/
87
- mkdir -p src/contacts/components/ArchivedContacts/
88
- ni src/contacts/components/AllContacts/index.tsx
89
- ni src/contacts/components/ArchivedContacts/index.tsx
90
- ```
91
-
92
- </TabItem>
93
- </Tabs>
94
-
95
- `ArchivedContacts/index.tsx` 的内容:
96
-
97
- ```js
70
+ ```tsx
71
+ import { Helmet } from '@modern-js/runtime/head';
98
72
  import { List } from 'antd';
99
- import Item from '../Item';
100
-
101
- const getAvatar = (users: Array<{ name: string; email: string }>) =>
102
- users.map(user => ({
103
- ...user,
104
- avatar: `https://avatars.dicebear.com/v2/identicon/${user.name}.svg`,
105
- }));
106
-
107
- const mockArchivedData = getAvatar([
108
- { name: 'Thomas', email: 'w.kccip@bllmfbgv.dm' },
109
- { name: 'Chow', email: 'f.lfqljnlk@ywoefljhc.af' },
110
- ]);
111
-
112
- const ArchivedContacts = () => (
113
- <List
114
- dataSource={mockArchivedData}
115
- renderItem={info => <Item key={info.name} info={info} />}
116
- />
117
- );
118
-
119
- export default ArchivedContacts;
120
- ```
121
-
122
- `AllContacts/index.tsx` 的内容:
73
+ import 'ladda/dist/ladda.min.css';
74
+ import 'tailwindcss/base.css';
75
+ import 'tailwindcss/components.css';
76
+ import 'tailwindcss/utilities.css';
77
+ import './styles/utils.css';
123
78
 
124
- ```tsx
125
79
  import { useState, useEffect } from 'react';
126
- import { List } from 'antd';
127
80
  import { get as contacts } from '@api/contacts';
128
- import Item from '../Item';
81
+ import Item from './components/Item';
129
82
 
130
- const AllContacts = () => {
83
+ function Index() {
131
84
  const [list, setList] = useState(
132
85
  [] as Array<{ name: string; email: string; avatar: string }>,
133
86
  );
@@ -141,20 +94,25 @@ const AllContacts = () => {
141
94
  }
142
95
  });
143
96
  return (
144
- (list.length && (
145
- <List
146
- dataSource={list}
147
- renderItem={info => <Item key={info.name} info={info} />}
148
- />
149
- )) || (
150
- <div className="p-4 items-center border-gray-200 border-b border-t custom-text-gray">
151
- Pending...
152
- </div>
153
- )
97
+ <div className="container lg mx-auto">
98
+ <Helmet>
99
+ <title>All</title>
100
+ </Helmet>
101
+ {(list.length && (
102
+ <List
103
+ dataSource={list}
104
+ renderItem={info => <Item key={info.name} info={info} />}
105
+ />
106
+ )) || (
107
+ <div className="p-4 items-center border-gray-200 border-b border-t custom-text-gray">
108
+ Pending...
109
+ </div>
110
+ )}
111
+ </div>
154
112
  );
155
- };
113
+ }
156
114
 
157
- export default AllContacts;
115
+ export default Index;
158
116
  ```
159
117
 
160
118
  在 Modern.js 中,可以通过调用函数(前后端一体化)的方式,直接调用 BFF 接口,调用时无需关心域名、路径等。
@@ -163,38 +121,6 @@ export default AllContacts;
163
121
  在使用 `pnpm run new` 创建 BFF 时,Modern.js 已经默认在 `tsconig.json` 中注入了别名。这也是之前提到的,生成器在项目创建之后并不会被抛弃,仍旧可以在开发过程中不断为项目提供新的内容。
164
122
  :::
165
123
 
166
- 接下来修改 `src/contacts/App.tsx`,把 `List` 组件的调用代码替换成上面的 `AllContacts` 和 `ArchivedContacts`:
167
-
168
- ```tsx
169
- <Route path="/" exact={true}>
170
- <Helmet>
171
- <title>All</title>
172
- </Helmet>
173
- <AllContacts />
174
- </Route>
175
- <Route path="/archives" exact={true}>
176
- <Helmet>
177
- <title>Archives</title>
178
- </Helmet>
179
- <ArchivedContacts />
180
- </Route>
181
- ```
182
-
183
- 删除 `App.tsx` 里的 mock 数据和 `List` 组件,把 `Item` 组件替换成 `ContactList`:
184
-
185
- ```tsx
186
- import { useState } from 'react';
187
- import { Radio, RadioChangeEvent } from 'antd';
188
- import { Route, Switch, useHistory } from '@modern-js/runtime/router';
189
- import { Helmet } from '@modern-js/runtime/head';
190
- import 'tailwindcss/base.css';
191
- import 'tailwindcss/components.css';
192
- import 'tailwindcss/utilities.css';
193
- import './styles/utils.css';
194
- import AllContacts from './components/AllContacts';
195
- import ArchivedContacts from './components/ArchivedContacts';
196
- ```
197
-
198
124
  执行 `pnpm run dev`,再打开页面`http://localhost:8000/contacts`,可以看到页面加载后,会先初始化联系人数据(显示 pending),之后每次切换到 All 列表,也会重新请求联系人数据:
199
125
 
200
126
  ![browser-result](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/08/browser-result.png)
@@ -2,11 +2,11 @@
2
2
  title: 应用架构
3
3
  ---
4
4
 
5
- 上一章节中,我们把硬编码的 `mockData` 改成从 BFF 加载,从 `App.tsx` 里拆分出了 `components/AllContacts.tsx` 和 `components/ArchivedContacts.tsx`,在 `AllContacts` 组件里用 BFF 函数,获取到联系人数据之后,保存在 `AllContacts` 的组件内部状态里,而 `ArchivedContacts` 组件暂时继续使用 mock 数据。
5
+ 上一章节中,我们把硬编码的 `mockData` 改成从 BFF 加载,在 `pages.tsx` 组件里用 BFF 函数,获取到联系人数据之后,保存在 `Index` 的组件内部状态里,而 `archives/index` 组件暂时继续使用 mock 数据。
6
6
 
7
- 本章节中,为了进一步实现项目功能,我们需要让 `AllContacts` 和 `ArchivedContacts` 都从 BFF 获取数据,还要实现【 Archive 】按钮,点击按钮能把联系人归档。
7
+ 本章节中,为了进一步实现项目功能,我们需要让两个组件都从 BFF 获取数据,还要实现【 Archive 】按钮,点击按钮能把联系人归档。
8
8
 
9
- 业务逻辑变复杂之后,相关代码不可避免会变多,如果都写在 AllContacts 组件或其他组件里,都会让这个组件的可读性和可维护性变差,让做不同事情的代码混在一起。
9
+ 业务逻辑变复杂之后,相关代码不可避免会变多,如果都写在组件里,都会让这个组件的可读性和可维护性变差,让做不同事情的代码混在一起。
10
10
 
11
11
  原本可以跟 UI 完全解耦的业务逻辑(比如网络请求、纯数据的操作、状态的管理等)跟 UI 逻辑(比如联系人列表如何展现)混在一起,当需要修改 UI 的时候,不得不跟 UI 无关的业务逻辑代码打交道,反过来也一样,不符合【 [单一职责原则(SRP)](https://zh.wikipedia.org/wiki/%E5%8D%95%E4%B8%80%E5%8A%9F%E8%83%BD%E5%8E%9F%E5%88%99)】,也做不到【 [关注点分离(SoC)](https://zh.wikipedia.org/wiki/%E5%85%B3%E6%B3%A8%E7%82%B9%E5%88%86%E7%A6%BB)】。
12
12
 
@@ -16,8 +16,6 @@ title: 应用架构
16
16
 
17
17
  Modern.js 提供开箱即用的**应用架构**,支持几种扮演不同**角色(role)**、属于不同**分层(layer)**的代码模块类型,可以把业务逻辑解耦到不同类型的代码模块里,做到高[内聚](https://zh.wikipedia.org/wiki/%E5%85%A7%E8%81%9A%E6%80%A7_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))低[耦合](https://zh.wikipedia.org/wiki/%E8%80%A6%E5%90%88%E6%80%A7_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。
18
18
 
19
- 之前介绍的 `App.tsx` 和 `pages/` 都是其中一种代码模块,通过在最顶层对客户端路由的管理,把其他代码模块最终组织到一起,形成应用。
20
-
21
19
  当前项目里 `components/` 目录中的 React 组件,是一种可以称作【 视图组件 】的代码模块,负责 UI、交互上的界面展现。
22
20
 
23
- 本章节我们来把 `AllContacts` 组件中可以跟 UI 解耦的业务逻辑,移到一种叫【 Model(业务模型)】的代码模块里。
21
+ 本章节我们来把 `page.tsx` 中的 Index 组件中可以跟 UI 解耦的业务逻辑,移到一种叫【 Model(业务模型)】的代码模块里。
@@ -121,11 +121,11 @@ const actions = {
121
121
  为了做到高内聚低耦合,一个 Model 的 state、action、side effect 不应该分散在不同文件里。接下来我们把上面的代码连起来,放在同一个 Model 文件里,执行以下命令:
122
122
 
123
123
  ```bash
124
- mkdir -p src/contacts/models/
125
- touch src/contacts/models/contacts.ts
124
+ mkdir -p src/contacts/routes/models/
125
+ touch src/contacts/routes/models/contacts.ts
126
126
  ```
127
127
 
128
- `src/contacts/models/contacts.ts` 的内容:
128
+ `src/contacts/routes/models/contacts.ts` 的内容:
129
129
 
130
130
  ```tsx
131
131
  import { model } from '@modern-js/runtime/model';
@@ -2,18 +2,17 @@
2
2
  title: 使用 Model
3
3
  ---
4
4
 
5
- 上一小节中,我们实现了 Model,已经把 AllContacts 组件中原有的 UI 无关业务逻辑解耦出来。
5
+ 上一小节中,我们实现了 Model,已经把 `page.tsx` `Index` 组件中原有的 UI 无关业务逻辑解耦出来。
6
6
 
7
- 这一小节中,我们使用这个 Model 直接把 AllContacts 组件重新还原出来,实现变得更简洁清晰:
7
+ 这一小节中,我们使用这个 Model 直接把 `page.tsx` `Index` 组件重新还原出来,实现变得更简洁清晰:
8
8
 
9
- ```js title="src/contacts/components/AllContacts/index.tsx"
9
+ ```js title="src/contacts/routes/page.tsx"
10
10
  import { useEffect } from 'react';
11
11
  import { useLocalModel } from '@modern-js/runtime/model';
12
- import { List } from 'antd';
13
- import Item from '../Item';
14
- import contacts from '../../models/contacts';
12
+ import contacts from './models/contacts';
13
+ import Item from './components/Item';
15
14
 
16
- const AllContacts = () => {
15
+ function Index() {
17
16
  const [{ items, error, pending }, actions] = useLocalModel(contacts);
18
17
 
19
18
  useEffect(() => {
@@ -22,23 +21,25 @@ const AllContacts = () => {
22
21
  }
23
22
  });
24
23
  return (
25
- (items.length && (
26
- <List
27
- dataSource={items}
28
- renderItem={info => <Item key={info.name} info={info} />}
29
- />
30
- )) || (
31
- <div className="p-4 items-center border-gray-200 border-b border-t custom-text-gray">
32
- Pending...
33
- </div>
34
- )
24
+ <div className="container lg mx-auto">
25
+ {(items.length && (
26
+ <List
27
+ dataSource={items}
28
+ renderItem={info => <Item key={info.name} info={info} />}
29
+ />
30
+ )) || (
31
+ <div className="p-4 items-center border-gray-200 border-b border-t custom-text-gray">
32
+ Pending...
33
+ </div>
34
+ )}
35
+ </div>
35
36
  );
36
- };
37
+ }
37
38
 
38
- export default AllContacts;
39
+ export default Index;
39
40
  ```
40
41
 
41
- 以上代码跟上一章节中 `AllContacts` 组件的代码等价。
42
+ 以上代码跟上一章节中 `Index` 组件的代码等价。
42
43
 
43
44
  `useLocalModel` 是 Modern.js 提供的 hooks API,可以使用 Model,在组件中提供 Model 中定义的 state,或通过 actions 调用 Model 中定义的 side effect 与 action,从而改变 Model 的 state。
44
45
 
@@ -17,14 +17,14 @@ import TabItem from '@theme/TabItem';
17
17
  <TabItem value="macOS" label="macOS" default>
18
18
 
19
19
  ```bash
20
- touch src/contacts/models/contacts.test.ts
20
+ touch src/contacts/routes/models/contacts.test.ts
21
21
  ```
22
22
 
23
23
  </TabItem>
24
24
  <TabItem value="Windows" label="Windows">
25
25
 
26
26
  ```powershell
27
- ni src/contacts/models/contacts.test.ts
27
+ ni src/contacts/routes/models/contacts.test.ts
28
28
  ```
29
29
 
30
30
  </TabItem>