@modern-js/main-doc 2.7.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +17 -0
- package/README.md +2 -2
- package/en/apis/app/commands.mdx +2 -0
- package/en/apis/app/runtime/model/connect.mdx +1 -1
- package/en/apis/app/runtime/model/model_.mdx +1 -1
- package/en/apis/app/runtime/model/use-model.mdx +1 -1
- package/en/apis/app/runtime/web-server/hook.mdx +2 -2
- package/en/apis/app/runtime/web-server/middleware.mdx +33 -9
- package/en/components/enable-bff.mdx +4 -4
- package/en/components/init-rspack-app.mdx +7 -0
- package/en/configure/app/bff/enable-handle-web.mdx +24 -0
- package/en/configure/app/server/enable-framework-ext.mdx +1 -1
- package/en/guides/advanced-features/bff/_category_.json +1 -1
- package/en/guides/advanced-features/eslint.mdx +30 -32
- package/en/guides/advanced-features/low-level.mdx +1 -1
- package/en/guides/advanced-features/rspack-start.mdx +13 -17
- package/en/guides/advanced-features/web-server.mdx +87 -20
- package/en/guides/concept/builder.mdx +1 -1
- package/en/guides/topic-detail/framework-plugin/extend.mdx +20 -19
- package/en/guides/topic-detail/framework-plugin/hook-list.mdx +156 -155
- package/en/guides/topic-detail/framework-plugin/hook.mdx +58 -43
- package/en/guides/topic-detail/framework-plugin/implement.mdx +47 -49
- package/en/guides/topic-detail/framework-plugin/introduction.mdx +22 -23
- package/en/guides/topic-detail/framework-plugin/plugin-api.mdx +13 -13
- package/en/guides/topic-detail/framework-plugin/relationship.mdx +30 -30
- package/en/guides/topic-detail/generator/plugin/develop.mdx +1 -1
- package/en/guides/topic-detail/micro-frontend/c01-introduction.mdx +17 -17
- package/en/guides/topic-detail/micro-frontend/c02-development.mdx +76 -78
- package/en/guides/topic-detail/micro-frontend/c03-main-app.mdx +57 -51
- package/en/guides/topic-detail/micro-frontend/c04-communicate.mdx +11 -11
- package/en/guides/topic-detail/micro-frontend/c05-mixed-stack.mdx +13 -13
- package/en/guides/topic-detail/model/auto-actions.mdx +18 -21
- package/en/guides/topic-detail/model/computed-state.mdx +27 -25
- package/en/guides/topic-detail/model/define-model.mdx +14 -14
- package/en/guides/topic-detail/model/faq.mdx +12 -13
- package/en/guides/topic-detail/model/manage-effects.mdx +43 -52
- package/en/guides/topic-detail/model/model-communicate.mdx +47 -45
- package/en/guides/topic-detail/model/performance.mdx +22 -23
- package/en/guides/topic-detail/model/quick-start.mdx +29 -28
- package/en/guides/topic-detail/model/redux-integration.mdx +7 -7
- package/en/guides/topic-detail/model/test-model.mdx +11 -11
- package/en/guides/topic-detail/model/typescript-best-practice.mdx +16 -15
- package/en/guides/topic-detail/model/use-model.mdx +40 -45
- package/en/guides/topic-detail/model/use-out-of-modernjs.mdx +16 -16
- package/en/guides/troubleshooting/cli.mdx +2 -2
- package/package.json +5 -5
- package/zh/apis/app/commands.mdx +2 -0
- package/zh/apis/app/runtime/model/connect.mdx +1 -1
- package/zh/apis/app/runtime/model/model_.mdx +1 -1
- package/zh/apis/app/runtime/model/use-model.mdx +1 -1
- package/zh/apis/app/runtime/web-server/hook.mdx +2 -4
- package/zh/apis/app/runtime/web-server/middleware.mdx +30 -10
- package/zh/apis/monorepo/commands/gen-release-note.mdx +3 -3
- package/zh/components/enable-bff.mdx +4 -4
- package/zh/components/init-rspack-app.mdx +7 -0
- package/zh/components/release-note.mdx +1 -1
- package/zh/configure/app/bff/enable-handle-web.mdx +24 -0
- package/zh/configure/app/server/enable-framework-ext.mdx +1 -1
- package/zh/guides/advanced-features/bff/_category_.json +1 -1
- package/zh/guides/advanced-features/rspack-start.mdx +13 -17
- package/zh/guides/advanced-features/web-server.mdx +81 -16
- package/zh/guides/concept/builder.mdx +1 -1
- package/zh/guides/topic-detail/changesets/github.mdx +2 -2
- package/zh/guides/topic-detail/changesets/release-note.mdx +1 -1
- package/zh/guides/topic-detail/framework-plugin/plugin-api.mdx +2 -2
- package/zh/guides/topic-detail/generator/plugin/develop.mdx +1 -1
- package/zh/guides/topic-detail/model/faq.mdx +1 -1
- package/zh/guides/topic-detail/model/manage-effects.mdx +1 -1
- package/zh/guides/topic-detail/model/model-communicate.mdx +1 -1
- package/zh/guides/topic-detail/model/performance.mdx +1 -1
- package/zh/guides/topic-detail/model/quick-start.mdx +2 -2
- package/zh/guides/topic-detail/model/use-model.mdx +3 -3
@@ -1,45 +1,45 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 3
|
3
|
-
title:
|
3
|
+
title: Develop Main App
|
4
4
|
---
|
5
|
-
#
|
5
|
+
# Develop Main App
|
6
6
|
|
7
|
-
|
7
|
+
In the previous [Experience micro frontend](/guides/topic-detail/micro-frontend/c02-development), an example was used to demonstrate how to create and configure micro frontend sub-applications. Through this chapter, you can further understand how to develop the main application, and its common configuration.
|
8
8
|
|
9
|
-
|
9
|
+
After creating an application project through the `@modern-js/create` command, you can execute `pnpm run new` in the project (the `modern new` command is actually executed). After selecting the 「micro frontend」 mode, the micro frontend will be installed. Dependencies, just register the plugin manually.
|
10
10
|
|
11
11
|
import EnableMicroFrontend from "@site-docs/components/enable-micro-frontend";
|
12
12
|
|
13
13
|
<EnableMicroFrontend />
|
14
14
|
|
15
|
-
##
|
15
|
+
## Register Sub-app
|
16
16
|
|
17
|
-
|
17
|
+
When the information is provided on the `masterApp` configuration, the application will be considered the main application. At present, there are two configuration methods for sub-app information, and these two methods are applied to different scenarios.
|
18
18
|
|
19
|
-
###
|
19
|
+
### Register sub-app info directly
|
20
20
|
|
21
|
-
|
21
|
+
You can register the sub-application information directly through the configuration:
|
22
22
|
|
23
23
|
:::tip
|
24
|
-
|
25
|
-
当参数为函数时无法被序列化到产物代码,所以在涉及到函数之类的配置时请通过 defineConfig 来进行配置
|
24
|
+
It can be configured at runtime via the API [defineConfig](/apis/app/runtime/app/define-config).
|
26
25
|
|
26
|
+
When the parameter is a function, it cannot be serialized to the product code, so please configure it through defineConfig when it comes to configuration such as functions
|
27
27
|
:::
|
28
28
|
|
29
29
|
import MicroRuntimeConfig from "@site-docs/components/micro-runtime-config";
|
30
30
|
|
31
31
|
<MicroRuntimeConfig />
|
32
32
|
|
33
|
-
###
|
33
|
+
### Custom remote app list
|
34
34
|
|
35
|
-
|
35
|
+
This function allows you to pull a list of remote child applications and register them with the runtime framework:
|
36
36
|
|
37
37
|
```ts title="App.tsx"
|
38
38
|
defineConfig(App, {
|
39
39
|
masterApp: {
|
40
40
|
manifest: {
|
41
41
|
getAppList: async () => {
|
42
|
-
//
|
42
|
+
// get from remote api
|
43
43
|
return [{
|
44
44
|
name: 'Table',
|
45
45
|
entry: 'http://localhost:8001',
|
@@ -55,24 +55,24 @@ defineConfig(App, {
|
|
55
55
|
});
|
56
56
|
```
|
57
57
|
|
58
|
-
##
|
58
|
+
## Renderer sub-app
|
59
59
|
|
60
|
-
|
60
|
+
There are two ways to load sub-app in micro frontend:
|
61
61
|
|
62
|
-
1.
|
63
|
-
2.
|
62
|
+
1. **Sub-app component ** Get the components of each sub-app, and then you can render the sub-app of micro frontend just like using ordinary'React 'components.
|
63
|
+
2. **Centralized routing** Through centralized routing configuration, the corresponding sub-app of rendering is automatically activated according to the current page `pathname`.
|
64
64
|
|
65
|
-
###
|
65
|
+
### Sub-app component
|
66
66
|
|
67
|
-
|
67
|
+
Developers can use the `useModuleApps` method to obtain the components of each child application.
|
68
68
|
|
69
|
-
|
69
|
+
Through the combined use of the `router` component, developers can autonomously render different sub-applications according to different routes.
|
70
70
|
|
71
|
-
|
71
|
+
Suppose our subapp list is configured as follows:
|
72
72
|
|
73
73
|
<EnableMicroFrontend />
|
74
74
|
|
75
|
-
|
75
|
+
`App.tsx` as follow:
|
76
76
|
|
77
77
|
```js title="App.tsx"
|
78
78
|
import { useModuleApps } from '@modern-js/plugin-garfish/runtime';
|
@@ -81,9 +81,9 @@ import { RouterProvider, Route, createBrowserRouter, createRoutesFromElements, B
|
|
81
81
|
|
82
82
|
const AppLayout = () => (
|
83
83
|
<>
|
84
|
-
<div><Link to={'/table'}
|
85
|
-
<div><Link to={'/dashboard'}
|
86
|
-
<div><Link to={'/'}
|
84
|
+
<div><Link to={'/table'}>load file-based sub-app</Link></div>
|
85
|
+
<div><Link to={'/dashboard'}>load self-controlled sub-app</Link></div>
|
86
|
+
<div><Link to={'/'}>unmount sub-app</Link></div>
|
87
87
|
<Outlet />
|
88
88
|
</>
|
89
89
|
)
|
@@ -91,13 +91,13 @@ const AppLayout = () => (
|
|
91
91
|
export default () => {
|
92
92
|
const { apps, MApp } = useModuleApps();
|
93
93
|
|
94
|
-
//
|
94
|
+
// Instead of using the MApp component, you need to use createBrowserRouter to create the route
|
95
95
|
const router = createBrowserRouter(
|
96
96
|
createRoutesFromElements(
|
97
97
|
<Route path="/" element={<AppLayout />}>
|
98
98
|
{apps.map(app => {
|
99
99
|
const { Component } = app;
|
100
|
-
//
|
100
|
+
// Fuzzy match, path needs to be written in a pattern similar to abc/*
|
101
101
|
return (
|
102
102
|
<Route
|
103
103
|
key={app.name}
|
@@ -125,12 +125,12 @@ export default () => {
|
|
125
125
|
);
|
126
126
|
|
127
127
|
return (
|
128
|
-
//
|
128
|
+
// Use MApp to automatically load sub-applications according to the configured activeWhen parameters (this project is configured in modern.config.ts)
|
129
129
|
// <BrowserRouter>
|
130
130
|
// <MApp />
|
131
131
|
// </BrowserRouter>
|
132
132
|
|
133
|
-
//
|
133
|
+
// Manually write the Route component to load the sub-application, which is convenient for scenarios that require pre-operation such as authentication
|
134
134
|
<>
|
135
135
|
<RouterProvider router={router} />
|
136
136
|
</>
|
@@ -138,17 +138,17 @@ export default () => {
|
|
138
138
|
};
|
139
139
|
```
|
140
140
|
|
141
|
-
|
141
|
+
Here, the activation route of **Table** is customized as **/table** through the `Route` component, and the activation route of **Dashboard** is **/dashboard**.
|
142
142
|
|
143
|
-
###
|
143
|
+
### Centralized routing
|
144
144
|
|
145
|
-
|
145
|
+
**Centralized Routing** is a way to centrally configure active routes for subapps. We enable **Centralized Routing** by adding an `activeWhen` field to the subapp list information.
|
146
146
|
|
147
147
|
<MicroRuntimeConfig />
|
148
148
|
|
149
|
-
|
149
|
+
Use `useModuleApp` api in Main App, get `MApp` component and then render it
|
150
150
|
|
151
|
-
```tsx title
|
151
|
+
```tsx title=Main App:App.tsx
|
152
152
|
import { useModuleApp } from '@modern-js/plugin-garfish/runtime';
|
153
153
|
|
154
154
|
function App() {
|
@@ -162,18 +162,22 @@ function App() {
|
|
162
162
|
}
|
163
163
|
```
|
164
164
|
|
165
|
-
|
165
|
+
After starting the application in this way, accessing the '/table' route will render the'Table 'sub-application, and accessing the'/dashboard 'route will render the'Dashboard' sub-application.
|
166
166
|
|
167
|
-
### 两种模式混用
|
168
167
|
|
169
|
-
|
168
|
+
### Mix Mode
|
170
169
|
|
171
|
-
- 一部分子应用作为 **子应用组件** 激活,另外一部分作为 **集中式路由** 激活。
|
172
|
-
- 一部分子应用既可以作为 **集中式路由** 激活,也可以作为 **子应用组件** 激活。
|
173
170
|
|
174
|
-
|
171
|
+
Of course, **sub-application components** and **centralized routing** can be mixed.
|
172
|
+
|
173
|
+
|
174
|
+
- One molecular application is activated as a **sub-application component**, and the other part is activated as a **centralized routing**.
|
175
|
+
- A molecular application can be activated either as a **centralized routing** or as a **sub-application component**.
|
176
|
+
|
177
|
+
### Loading
|
178
|
+
|
179
|
+
By configuring the `loadable` configuration, loading components can be added for 「centralized routing」 and 「sub-applicati」, and errors, timeouts, flashes, etc. can be considered, so as to provide users with a better user experience. The design and implementation of this function refer to [react-loadable](https://github.com/jamiebuilds/react-loadable), and the basic functions are similar.
|
175
180
|
|
176
|
-
通过配置 `loadable` 配置,可以为「集中式路由」、「子应用」添加 loading 组件,并可以考虑错误、超时、闪烁等情况的出现,从而为用户提供更好的用户体验。该功能的设计与实现参考至 [react-loadable](https://github.com/jamiebuilds/react-loadable),基本功能较为相似。
|
177
181
|
|
178
182
|
```tsx
|
179
183
|
function Loading() {
|
@@ -191,9 +195,9 @@ function App(){
|
|
191
195
|
}
|
192
196
|
```
|
193
197
|
|
194
|
-
####
|
198
|
+
#### Add Error Status
|
195
199
|
|
196
|
-
|
200
|
+
When the micro-frontend sub-application fails to load or render, the `loading component` will receive the `error` parameter (if there is no error, the error is null).
|
197
201
|
|
198
202
|
```tsx
|
199
203
|
function Loading({ error }) {
|
@@ -205,11 +209,13 @@ function Loading({ error }) {
|
|
205
209
|
}
|
206
210
|
```
|
207
211
|
|
208
|
-
####
|
212
|
+
#### Avoid Loading Flash Back
|
213
|
+
|
214
|
+
Sometimes the display time of the loading component may be less than 200ms, and the loading component will flash back at this time. Many user studies have proved that the loading flash back situation will cause the user to perceive that the loading content takes longer than the actual time.
|
209
215
|
|
210
|
-
|
216
|
+
When loading is less than 200ms, if the content is not displayed, the user will think it is faster.
|
211
217
|
|
212
|
-
|
218
|
+
Therefore, the loading component also provides the `pastDelay` parameter, which will only be true when it exceeds the set delay display. You can set the delay duration through the `delay` parameter.
|
213
219
|
|
214
220
|
```tsx
|
215
221
|
function Loading({ error, pastDelay }) {
|
@@ -223,7 +229,7 @@ function Loading({ error, pastDelay }) {
|
|
223
229
|
}
|
224
230
|
```
|
225
231
|
|
226
|
-
`delay`
|
232
|
+
The default value of `delay` is `200ms`, you can set the delay display time through `delay` in `loadable`.
|
227
233
|
|
228
234
|
```tsx
|
229
235
|
|
@@ -239,11 +245,11 @@ function App(){
|
|
239
245
|
}
|
240
246
|
```
|
241
247
|
|
242
|
-
####
|
248
|
+
#### Add Timeout State
|
243
249
|
|
244
|
-
|
250
|
+
Sometimes because of the network, the micro-front-end sub-application fails to load, resulting in the loading state being displayed all the time, which is very bad for users, because they don't know the right response to get a specific response, whether they need to refresh the page, by Increasing the timeout state can solve this problem well.
|
245
251
|
|
246
|
-
loading
|
252
|
+
The loading component will get the `timeOut` parameter when timeout, when the micro frontend application loads timeout, it will get the `timeOut` property value of true.
|
247
253
|
|
248
254
|
```tsx
|
249
255
|
function Loading({ error, timeOut, pastDelay }) {
|
@@ -259,7 +265,7 @@ function Loading({ error, timeOut, pastDelay }) {
|
|
259
265
|
}
|
260
266
|
```
|
261
267
|
|
262
|
-
|
268
|
+
The timeout state is off and can be enabled by setting the `timeout` parameter in `loadable`:
|
263
269
|
|
264
270
|
```tsx
|
265
271
|
|
@@ -1,14 +1,14 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 4
|
3
|
-
title:
|
3
|
+
title: Communicate
|
4
4
|
---
|
5
|
-
#
|
5
|
+
# Communicate
|
6
6
|
|
7
|
-
##
|
7
|
+
## Props
|
8
8
|
|
9
|
-
Modern.js
|
9
|
+
In Modern.js, the sub-application will be wrapped into a React component, and the purpose of communicating between the main application and the sub-application can be achieved directly by passing'props' to the React component.
|
10
10
|
|
11
|
-
```tsx title
|
11
|
+
```tsx title=Main:App.tsx
|
12
12
|
function App() {
|
13
13
|
const { MApp } = useModuleApps();
|
14
14
|
|
@@ -20,21 +20,21 @@ function App() {
|
|
20
20
|
}
|
21
21
|
```
|
22
22
|
|
23
|
-
```tsx title
|
23
|
+
```tsx title=Main:App.tsx
|
24
24
|
function App(props) {
|
25
25
|
console.log(props);
|
26
26
|
return ...
|
27
27
|
}
|
28
28
|
```
|
29
29
|
|
30
|
-
|
30
|
+
The child application will print `{count: 0}`. Currently, child application rendering responsiveness is not supported. Changing the data of'count 'on the main application in time will not trigger a view update
|
31
31
|
|
32
|
-
## channel
|
32
|
+
## channel
|
33
33
|
|
34
|
-
[Garfish.channel](https://www.garfishjs.org/api/channel)
|
34
|
+
[Garfish.channel] (https://www.garfishjs.org/api/channel) Used for communication between applications. It is an instance of `EventEmitter2`.
|
35
35
|
|
36
36
|
```ts
|
37
|
-
//
|
37
|
+
// sub app listen login event
|
38
38
|
const App = () => {
|
39
39
|
const handleLogin = userInfo => {
|
40
40
|
console.log(`${userInfo.name} has login`);
|
@@ -48,7 +48,7 @@ const App = () => {
|
|
48
48
|
});
|
49
49
|
};
|
50
50
|
|
51
|
-
//
|
51
|
+
// main app emit login event
|
52
52
|
api.getLoginInfo.then(res => {
|
53
53
|
if (res.code === 0) {
|
54
54
|
window.Garfish.channel.emit('login', res.data);
|
@@ -1,26 +1,26 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 5
|
3
|
-
title:
|
3
|
+
title: Mixed Stack
|
4
4
|
---
|
5
|
-
#
|
5
|
+
# Mixed Stack
|
6
6
|
|
7
|
-
Modern.js
|
7
|
+
The Modern.js micro frontend scheme is based on the [Garfish](https://garfishjs.org/) package and provides some more out-of-the-box usage.
|
8
8
|
|
9
|
-
|
9
|
+
When your main application and sub-application are not all Modern.js applications, you can refer to this document.
|
10
10
|
|
11
|
-
1.
|
12
|
-
2.
|
11
|
+
1. The sub-app is **Modern.js**, the native Garfish micro frontend used by the main app.
|
12
|
+
2. The main application is **Modern.js**, and some sub-applications have other technology stacks.
|
13
13
|
|
14
|
-
##
|
14
|
+
## Modern.js Sub App
|
15
15
|
|
16
|
-
**Modern.js**
|
17
|
-
|
16
|
+
**Modern.js** subapps compile to generate a standard [Garfish subapp export](https://www.garfishjs.org/guide/start#2%E5%AF%BC%E5%87%BA-provider-%E5%87%BD%E6%95%B0).
|
17
|
+
So you can directly access the standard micro frontend main application.
|
18
18
|
|
19
|
-
:::info
|
20
|
-
子应用是 **Modern.js**,主应用使用的原生 Garfish 微前端时,**子应用调试模式** 不可用。
|
21
19
|
|
20
|
+
:::info
|
21
|
+
The child application is **Modern.js**, when the main application uses the native Garfish micro frontend, the **child application debugging mode** is not available.
|
22
22
|
:::
|
23
23
|
|
24
|
-
##
|
24
|
+
## Modern.js Main App
|
25
25
|
|
26
|
-
|
26
|
+
The Main App is **Modern.js**, and other technology stacks used by sub-applications. Sub-App can be developed according to [Garfish Sub-Application Standard](https://www.garfishjs.org/guide/demo/react).
|
@@ -1,23 +1,22 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 6
|
3
|
-
title:
|
3
|
+
title: Automatically Actions
|
4
4
|
---
|
5
|
-
#
|
5
|
+
# Automatically Generated Actions
|
6
6
|
|
7
|
-
|
8
|
-
实际上,Modern.js 支持根据声明的 `state` 类型,自动生成常用的 Actions,从而简化模板代码量。当前支持的类型有:
|
7
|
+
In the [Quick Start](/guides/topic-detail/model/quick-start), we implemented the simplest counter model, which still required 10 lines of code. In fact, Modern.js supports automatically generating commonly used actions based on the declared `state` type, which reduces the amount of boilerplate code. The currently supported types are:
|
9
8
|
|
10
|
-
-
|
11
|
-
-
|
12
|
-
-
|
9
|
+
- Primitive data types
|
10
|
+
- Array types
|
11
|
+
- Simple object types (Plain Object)
|
13
12
|
|
14
|
-
##
|
13
|
+
## Primitive data types
|
15
14
|
|
16
15
|
```ts
|
17
16
|
const countModel = model('count').define({ state: 1 });
|
18
17
|
```
|
19
18
|
|
20
|
-
|
19
|
+
As shown above, we only need one line to create a simple `countModel`. Here's an example code that uses the model:
|
21
20
|
|
22
21
|
```tsx
|
23
22
|
function Counter() {
|
@@ -30,9 +29,9 @@ function Counter() {
|
|
30
29
|
}
|
31
30
|
```
|
32
31
|
|
33
|
-
##
|
32
|
+
## Array Types
|
34
33
|
|
35
|
-
|
34
|
+
When the state is an array type, an example code for automatically generating actions is shown below:
|
36
35
|
|
37
36
|
```ts
|
38
37
|
const countModel = model('count').define({ state: [] });
|
@@ -52,11 +51,11 @@ function Counter() {
|
|
52
51
|
}
|
53
52
|
```
|
54
53
|
|
55
|
-
|
54
|
+
We can use the methods of the JavaScript Array object to modify the state.
|
56
55
|
|
57
|
-
##
|
56
|
+
## Simple Object Types
|
58
57
|
|
59
|
-
|
58
|
+
When the state is a simple object type, an example code for automatically generating actions is shown below:
|
60
59
|
|
61
60
|
```ts
|
62
61
|
const countModel = model('count').define({
|
@@ -78,15 +77,13 @@ function Counter() {
|
|
78
77
|
}
|
79
78
|
```
|
80
79
|
|
81
|
-
|
80
|
+
Three different fields `a`, `b`, and `c` each generate `setA`, `setB`, and `setC` actions, respectively.
|
82
81
|
|
83
82
|
:::info
|
84
|
-
|
85
|
-
在 `countModel` 中已经自定义 `setA` 这个 Action,调用 `actions.setA()` 时,最终执行的是用户自定义的 `setA`。
|
86
|
-
|
83
|
+
When the user-defined action and the action automatically generated by Modern.js have the same name, the user-defined action takes precedence. For example, if `setA` action is already defined in `countModel`, calling `actions.setA()` executes the user-defined `setA`.
|
87
84
|
:::
|
88
85
|
|
89
|
-
:::info
|
90
|
-
|
91
|
-
|
86
|
+
:::info Additional Information
|
87
|
+
For more information on related APIs, please refer to [here](/apis/app/runtime/model/auto-actions).
|
92
88
|
:::
|
89
|
+
|
@@ -1,32 +1,32 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 4
|
3
|
-
title:
|
3
|
+
title: Derived State
|
4
4
|
---
|
5
|
-
# 衍生状态
|
6
5
|
|
7
|
-
|
8
|
-
衍生状态定义在 Model 中的 `computed` 字段下。根据依赖的 Model 的不同、返回类型的不同,衍生状态的定义方法可以分为以下 3 种。
|
6
|
+
# Derived State
|
9
7
|
|
10
|
-
|
8
|
+
In some scenarios, components need to perform further calculations on the State in Model before they can be used in the components. This logic can be directly written in the component or implemented through derived states in Model. Derived states are defined under the `computed` field in the Model. Depending on the dependencies of the Model and the return type, there are three ways to define derived states.
|
11
9
|
|
12
|
-
|
10
|
+
## Only Depend on the Model's Own State
|
13
11
|
|
14
|
-
|
12
|
+
The derived state only depends on the current Model's State, which is passed as the first parameter to the derived state's definition function.
|
13
|
+
|
14
|
+
For example, the todo application has `items` and `filter` in its State, and `filter` is used to filter the todo items displayed on the current page. Therefore, we define a `visibleTodos` derived state that can be directly used in the component. The sample code is as follows:
|
15
15
|
|
16
16
|
```ts
|
17
17
|
/**
|
18
|
-
*
|
18
|
+
* Assuming the structure of the todo item is as follows:
|
19
19
|
{
|
20
20
|
id: string; // id
|
21
|
-
text: string; // todo
|
22
|
-
completed: boolean;
|
21
|
+
text: string; // todo
|
22
|
+
completed: boolean;
|
23
23
|
}
|
24
24
|
**/
|
25
25
|
|
26
26
|
const todoModel = model('todo').define({
|
27
27
|
state: {
|
28
28
|
items: [],
|
29
|
-
filter: 'ALL', // ALL:
|
29
|
+
filter: 'ALL', // ALL: show all;COMPLETED:show completed;ACTIVE:show active
|
30
30
|
},
|
31
31
|
computed: {
|
32
32
|
visibleTodos: state => {
|
@@ -45,7 +45,7 @@ const todoModel = model('todo').define({
|
|
45
45
|
});
|
46
46
|
```
|
47
47
|
|
48
|
-
|
48
|
+
Derived state will eventually be merged with the Model's State, so the derived state can be accessed through the Model's State object. For example, the `visibleTodos` can be used in the component as follows:
|
49
49
|
|
50
50
|
```ts
|
51
51
|
function Todo() {
|
@@ -63,15 +63,17 @@ function Todo() {
|
|
63
63
|
}
|
64
64
|
```
|
65
65
|
|
66
|
-
##
|
66
|
+
## Dependent State from Other Models
|
67
|
+
|
68
|
+
In addition to depending on the current model's state, derived states may also depend on the state of other models. In this case, the definition format for the derived state is:
|
67
69
|
|
68
|
-
除了依赖当前 Model 的 State,衍生状态还依赖其他 Model 的 State,这时候衍生状态的定义格式为:
|
69
70
|
|
70
71
|
```ts
|
71
72
|
[stateKey]: [...depModels, (selfState, ...depModels) => computedState]
|
72
73
|
```
|
73
74
|
|
74
|
-
|
75
|
+
The following example demonstrates how the derived state `combinedValue` of `barModel` depends on the state of `fooModel`.
|
76
|
+
|
75
77
|
|
76
78
|
```ts
|
77
79
|
const fooModel = model('foo').define({
|
@@ -93,16 +95,17 @@ const barModel = model('bar').define({
|
|
93
95
|
});
|
94
96
|
```
|
95
97
|
|
96
|
-
##
|
98
|
+
## Derived State with Function Type
|
97
99
|
|
98
|
-
|
100
|
+
Derived states can also return a function. In this case, the definition format for the derived state is:
|
99
101
|
|
100
102
|
```ts
|
101
|
-
[stateKey]: (state) => (...args) => computedState //
|
102
|
-
[stateKey]: [...depModels, (selfState, ...depModels) => (...args) => computedState] //
|
103
|
+
[stateKey]: (state) => (...args) => computedState // Only relies on its own state
|
104
|
+
[stateKey]: [...depModels, (selfState, ...depModels) => (...args) => computedState] // Relies on the state of other models
|
103
105
|
```
|
104
106
|
|
105
|
-
|
107
|
+
Assuming the `filter` state is not stored in the state of the todo app, but is instead used directly in the component, `visibleTodos` can be a function type value. This function receives the `filter` parameter when used in the component, as shown below:
|
108
|
+
|
106
109
|
|
107
110
|
```ts
|
108
111
|
const todoModel = model('todo').define({
|
@@ -126,11 +129,11 @@ const todoModel = model('todo').define({
|
|
126
129
|
});
|
127
130
|
|
128
131
|
function Todo(props) {
|
129
|
-
//
|
132
|
+
// use props pass filter
|
130
133
|
const { filter } = props;
|
131
134
|
const [state, actions] = useModel(todoModel);
|
132
135
|
|
133
|
-
//
|
136
|
+
// get final todos
|
134
137
|
const todos = state.visibleTodos(filter);
|
135
138
|
|
136
139
|
return (
|
@@ -145,7 +148,6 @@ function Todo(props) {
|
|
145
148
|
}
|
146
149
|
```
|
147
150
|
|
148
|
-
:::info
|
149
|
-
[
|
150
|
-
|
151
|
+
:::info Additional Reference
|
152
|
+
[Using Models](/guides/topic-detail/model/computed-state)
|
151
153
|
:::
|
@@ -1,12 +1,13 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 2
|
3
|
-
title:
|
3
|
+
title: Define Model
|
4
4
|
---
|
5
|
-
#
|
5
|
+
# Define a Model
|
6
6
|
|
7
|
-
|
7
|
+
In the previous section, we demonstrated how to create a Model for a simple counter application. In this section, we will provide a detailed introduction on how to create a Model.
|
8
|
+
|
9
|
+
We can create a Model using the `model` API. For example, `model('foo')` creates a Model named `foo`. We can define the State and Actions included in the Model by calling the `define` function returned by `model('foo')`:
|
8
10
|
|
9
|
-
通过 `model` API 创建 Model,例如,`model('foo')` 表示要创建一个名为 `foo` 的 Model,通过调用 `model('foo')` 返回的 `define` 函数,定义 Model 包含的 State、Actions 等:
|
10
11
|
|
11
12
|
```js
|
12
13
|
import { model, useModel } from '@modern-js/runtime/model';
|
@@ -24,14 +25,14 @@ const fooModel = model('foo').define({
|
|
24
25
|
```
|
25
26
|
|
26
27
|
:::info
|
27
|
-
- Model
|
28
|
-
- `setValue`
|
28
|
+
- Actions in the Model cannot contain side-effect logic, such as requesting HTTP interfaces, accessing localStorage, etc.
|
29
|
+
- `setValue` directly modifies the input State, which seems to violate the definition of a pure function. However, Reduck uses [immer](https://github.com/immerjs/immer) internally to modify immutable objects, ensuring that this approach does not affect the immutability of the object, so `setValue` is still a pure function. Of course, you can also return a new object directly in the Action, but this approach will be more complex.
|
29
30
|
|
30
31
|
:::
|
31
32
|
|
32
|
-
`define`
|
33
|
+
The `define` function only describes the original structure of the Model, including the internally defined State, Actions, and so on. The `fooModel` returned by `define` is the actual Model object that is created. For example, although `setValue` has two parameters, when we call the `setValue` Action, we only need to pass in the `payload` parameter, because we are calling the Action method on `fooModel`, not the one described in the original structure of the Model. For more information, please refer to [Using Models](/guides/topic-detail/model/use-model).
|
33
34
|
|
34
|
-
`define`
|
35
|
+
In addition to object-type parameters, `define` can also receive function-type parameters. For example:
|
35
36
|
|
36
37
|
```js
|
37
38
|
import { model, useModel } from '@modern-js/runtime/model';
|
@@ -50,17 +51,16 @@ const fooModel = model('foo').define((context, utils) => {
|
|
50
51
|
});
|
51
52
|
```
|
52
53
|
|
53
|
-
|
54
|
+
When defining a Model using a function, the function automatically receives two parameters: `context` and `utils`. `context` is the context object of Reduck, which can obtain the `store` object, and `utils` provides a set of utility functions to facilitate the implementation of complex features such as Model communication.
|
54
55
|
|
55
|
-
|
56
|
+
Models support duplication. For example:
|
56
57
|
|
57
58
|
```ts
|
58
59
|
const barModel = fooModel('bar');
|
59
60
|
```
|
60
61
|
|
61
|
-
barModel
|
62
|
-
|
63
|
-
:::info 补充信息
|
64
|
-
本节涉及的 API 的详细定义,请参考[这里](/apis/app/runtime/model/model_)。
|
62
|
+
`barModel` is a new Model object created based on `fooModel`. Analogous to the concepts in object-oriented programming languages, `barModel` and `fooModel` are two instance objects created based on the same class. When the state management logic of two modules is the same, for example, two tab modules in the same page use the same data structure and logic, but the difference is that they obtain data from different interfaces, then you can create two different Model objects by duplicating the Model.
|
65
63
|
|
64
|
+
:::info Additional Information
|
65
|
+
For detailed definitions of the APIs mentioned in this section, please refer to [here](/apis/app/runtime/model/model_).
|
66
66
|
:::
|