@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,24 +1,23 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 13
|
3
|
-
title:
|
3
|
+
title: FAQ
|
4
4
|
---
|
5
5
|
|
6
|
-
#
|
6
|
+
# FAQ
|
7
7
|
|
8
|
-
##
|
8
|
+
## Browser Compatibility
|
9
9
|
|
10
|
-
Reduck
|
11
|
-
|
12
|
-
:::info 补充信息
|
13
|
-
Reduck 使用的 [`@babel/preset-env`](https://babeljs.io/docs/en/babel-preset-env) 的详细[配置](https://github.com/modern-js-dev/reduck/blob/main/common/config.js#L10~L17)。
|
10
|
+
The compiled build artifacts of Reduck use ES6 syntax by default. If you need to support older versions of browsers, please include all packages under the `@modern-js-reduck` namespace in your application's build process.
|
14
11
|
|
12
|
+
:::info Additional Information
|
13
|
+
Reduck uses [`@babel/preset-env`](https://babeljs.io/docs/en/babel-preset-env) with [specific configurations](https://github.com/web-infra-dev/reduck/blob/main/common/config.js#L10~L17).
|
15
14
|
:::
|
16
15
|
|
17
|
-
##
|
16
|
+
## Accessing the Main Application Model from a Microfrontend Sub-Application Model
|
18
17
|
|
19
|
-
|
18
|
+
When a microfrontend sub-application model accesses the main application model and the model has not yet been mounted on the main application, it will be automatically mounted on the sub-application.
|
20
19
|
|
21
|
-
|
20
|
+
Example:
|
22
21
|
|
23
22
|
```ts
|
24
23
|
import { useModel } from '@modern-js/runtime/model';
|
@@ -31,11 +30,11 @@ function SubModelApp() {
|
|
31
30
|
}
|
32
31
|
```
|
33
32
|
|
34
|
-

|
35
34
|
|
36
|
-
|
35
|
+
To avoid accidental downgrades, it is recommended to pre-mount the model that the main application needs to share:
|
37
36
|
|
38
37
|
```ts
|
39
|
-
// App
|
38
|
+
// App is the entry component of the main application, and sharedModel1 and sharedModel2 are the models that need to be shared.
|
40
39
|
App.models = [sharedModel1, sharedModel2];
|
41
40
|
```
|
@@ -1,16 +1,16 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 5
|
3
|
-
title:
|
3
|
+
title: Management Effect
|
4
4
|
---
|
5
|
-
#
|
5
|
+
# Management Effect
|
6
6
|
|
7
|
-
|
7
|
+
The actions in the model must be pure functions and cannot have any side effects during execution. However, in real-world scenarios, we often encounter many side effects, such as: requesting data from an HTTP API to obtain state data, or modifying `localStorage` or sending events while updating the state. In Reduck, side effects are managed through the model's `effects` function.
|
8
8
|
|
9
|
-
##
|
9
|
+
## Modifying State via Side Effects
|
10
10
|
|
11
|
-
|
11
|
+
The most common scenario in which side effects modify the state is requesting data from an HTTP API to update state data.
|
12
12
|
|
13
|
-
|
13
|
+
Let's take a simple `todoModel` as an example. It has a side effect function `load` that requests the TODO list from a remote server. After the request succeeds, it updates the `state.items` field.
|
14
14
|
|
15
15
|
```ts
|
16
16
|
const todoModel = model('todo').define({
|
@@ -35,7 +35,7 @@ const todoModel = model('todo').define({
|
|
35
35
|
},
|
36
36
|
},
|
37
37
|
effects: {
|
38
|
-
// Promise
|
38
|
+
// Promise effects
|
39
39
|
async load() {
|
40
40
|
return new Promise(resolve => {
|
41
41
|
setTimeout(() => resolve(['Learn Modern.js']), 2000);
|
@@ -45,22 +45,22 @@ const todoModel = model('todo').define({
|
|
45
45
|
});
|
46
46
|
```
|
47
47
|
|
48
|
-
|
48
|
+
The side effect function is uniformly defined under the `effects` field. Here we have written a `load` function that returns a Promise. After the Promise is successfully executed, it returns the TODO list `["Lerna Modern.js"]`.
|
49
49
|
|
50
|
-
|
50
|
+
Side effect functions need to work with actions to modify state. Therefore, we define a `load` object in `actions` (the name needs to be consistent with the name of the side effect function under `effects`), which includes three actions (`pending`, `fulfilled`, `rejected`) that handle the three states (`pending`, `fulfilled`, `rejected`) of the `Promise` returned by the side effect function `load`:
|
51
51
|
|
52
|
-
- `pending
|
53
|
-
- `fulfilled
|
54
|
-
- `rejected
|
52
|
+
- `pending`: Receives the current state `state` as a parameter and sets the `loading` flag to `true` in the new state.
|
53
|
+
- `fulfilled`: Receives the current state `state` and the Promise fulfilled value `items` as parameters, and sets `items` to the parameter `items` and `loading` to `false` in the new state.
|
54
|
+
- `rejected`: Receives the current state `state` and the Promise rejected error `error` as parameters, and sets `error` to the parameter `error` and `loading` to `false` in the new state.
|
55
55
|
|
56
|
-
|
56
|
+
How do we call the effects function in the component? The effects function will be merged into the actions object, so you can call the effects function through the actions object, as shown below:
|
57
57
|
|
58
58
|
```ts
|
59
59
|
function Todo() {
|
60
60
|
const [state, actions] = useModel(todoModel);
|
61
61
|
|
62
62
|
useEffect(() => {
|
63
|
-
//
|
63
|
+
// invoke effects function
|
64
64
|
actions.load();
|
65
65
|
}, []);
|
66
66
|
|
@@ -80,9 +80,9 @@ function Todo() {
|
|
80
80
|
}
|
81
81
|
```
|
82
82
|
|
83
|
-
|
83
|
+
In the example above, the three actions `pending`, `fulfilled`, and `rejected` are generally required for HTTP requests used to obtain data. Reduck provides a utility function `handleEffect` to simplify the creation of actions in this scenario.
|
84
84
|
|
85
|
-
`handleEffect`
|
85
|
+
For this type of side effect scenario, `handleEffect` stipulates that the state structure of the model contains three fields: `result`, `error`, and `pending`, with initial values of:
|
86
86
|
|
87
87
|
```ts
|
88
88
|
{
|
@@ -92,7 +92,7 @@ function Todo() {
|
|
92
92
|
}
|
93
93
|
```
|
94
94
|
|
95
|
-
|
95
|
+
Calling `handleEffect` will return the following data structure:
|
96
96
|
|
97
97
|
```ts
|
98
98
|
{
|
@@ -102,9 +102,9 @@ function Todo() {
|
|
102
102
|
}
|
103
103
|
```
|
104
104
|
|
105
|
-
|
105
|
+
This data structure is the same as the data structure of the `load` object under `actions`. The object returned by `handleEffect` actually corresponds to the three actions required by the Effects function.
|
106
106
|
|
107
|
-
|
107
|
+
We can use `handleEffect` to rewrite `todoModel`:
|
108
108
|
|
109
109
|
```ts
|
110
110
|
const todoModel = model('todo').define({
|
@@ -117,7 +117,7 @@ const todoModel = model('todo').define({
|
|
117
117
|
load: handleEffect({ result: 'items' }),
|
118
118
|
},
|
119
119
|
effects: {
|
120
|
-
// Promise
|
120
|
+
// Promise effects
|
121
121
|
async load() {
|
122
122
|
return new Promise(resolve => {
|
123
123
|
setTimeout(() => resolve(['Learn Modern.js']), 2000);
|
@@ -127,29 +127,26 @@ const todoModel = model('todo').define({
|
|
127
127
|
});
|
128
128
|
```
|
129
129
|
|
130
|
-
`handleEffect`
|
130
|
+
The `handleEffect` function sets `result` to `items` in the received parameter object. Because `todoModel` uses `items` as the key to save the TODO list data instead of using the default `result` as the key of `handleEffect`, configuration is required here.
|
131
131
|
|
132
|
-
|
132
|
+
It is obvious that the `todoModel` implemented through `handleEffect` is much more concise than the previous implementation.
|
133
133
|
|
134
|
-
|
134
|
+
If you do not want all three states (pending, fulfilled, rejected) to be automatically handled by `handleEffect`, for example, if the fulfilled state requires more complex data processing and needs to be manually handled, but you still want to automate the handling of the pending and rejected states, you can use the following method:
|
135
135
|
|
136
136
|
```ts
|
137
137
|
actions: {
|
138
138
|
load: {
|
139
139
|
...handleEffect(),
|
140
140
|
fulfilled(state, payload) {
|
141
|
-
//
|
141
|
+
// manual handle
|
142
142
|
},
|
143
143
|
},
|
144
144
|
},
|
145
145
|
```
|
146
146
|
|
147
|
-
|
148
|
-
`handleEffect` [API](/apis/app/runtime/model/handle-effect)。
|
147
|
+
`handleEffect` [API](/apis/app/runtime/model/handle-effect).
|
149
148
|
|
150
|
-
|
151
|
-
|
152
|
-
Effects 函数中,也支持手动调用 Actions,例如:
|
149
|
+
In the Effects function, you can also manually call Actions. For example:
|
153
150
|
|
154
151
|
```ts
|
155
152
|
const todoModel = model('todo').define((context, utils) => ({
|
@@ -169,15 +166,15 @@ const todoModel = model('todo').define((context, utils) => ({
|
|
169
166
|
},
|
170
167
|
effects: {
|
171
168
|
async load() {
|
172
|
-
//
|
169
|
+
// use utils.use get cuttent Model actions
|
173
170
|
const [, actions] = utils.use(todoModel);
|
174
|
-
//
|
171
|
+
// invoke action
|
175
172
|
actions.pending();
|
176
173
|
|
177
174
|
return new Promise(resolve => {
|
178
175
|
setTimeout(() => {
|
179
176
|
const items = ['Learn Modern.js'];
|
180
|
-
//
|
177
|
+
// invoke action
|
181
178
|
actions.fulfilled(items);
|
182
179
|
resolve(items);
|
183
180
|
}, 2000);
|
@@ -187,16 +184,13 @@ const todoModel = model('todo').define((context, utils) => ({
|
|
187
184
|
}));
|
188
185
|
```
|
189
186
|
|
190
|
-
|
191
|
-
可以使用 `use` 函数加载其它 Model(包括 Model 自身),实现 [Model 间通信](/guides/topic-detail/model/model-communicate)。
|
192
|
-
|
193
|
-
:::
|
187
|
+
You can use the `use` function to load other Models (including the Model itself) to achieve [Model communication](/guides/topic-detail/model/model-communicate).
|
194
188
|
|
195
|
-
##
|
189
|
+
## Side effects do not affect state
|
196
190
|
|
197
|
-
|
191
|
+
In some cases, we only need to read State and execute relevant side effect logic, and the side effect will not modify State.
|
198
192
|
|
199
|
-
|
193
|
+
For example, store some State in `localStorage`:
|
200
194
|
|
201
195
|
```ts
|
202
196
|
const fooModel = model('foo').define((context, utils) => ({
|
@@ -213,7 +207,7 @@ const fooModel = model('foo').define((context, utils) => ({
|
|
213
207
|
}));
|
214
208
|
```
|
215
209
|
|
216
|
-
|
210
|
+
or send message to server:
|
217
211
|
|
218
212
|
```ts
|
219
213
|
const fooModel = model('foo').define({
|
@@ -232,29 +226,26 @@ const fooModel = model('foo').define({
|
|
232
226
|
});
|
233
227
|
```
|
234
228
|
|
235
|
-
|
236
|
-
|
237
|
-
有时候,我们希望能根据副作用函数的执行结果,直接执行后续逻辑。这时候,就需要使用 Effects 函数的返回。
|
229
|
+
Sometimes, we want to execute subsequent logic directly based on the execution result of the side effect function. In this case, we need to use the return value of the Effects function.
|
238
230
|
|
239
|
-
|
231
|
+
For example, when the send button is clicked and the data is successfully sent, close the current popup immediately; if it fails, display an error message. We can achieve this through the following code:
|
240
232
|
|
241
233
|
```ts
|
242
|
-
//
|
243
|
-
//
|
234
|
+
// The code is for illustration only and cannot be executed.
|
235
|
+
// Response function of the "send" button within the component.
|
244
236
|
const handleClick = async () => {
|
245
|
-
// sendData
|
237
|
+
// sendData returns a string that represents the state.
|
246
238
|
const result = await actions.sendData('some data');
|
247
239
|
if (result === 'success') {
|
248
|
-
//
|
240
|
+
// Close the popup window.
|
249
241
|
closeModal();
|
250
242
|
} else {
|
251
|
-
//
|
243
|
+
// show error
|
252
244
|
showError(result);
|
253
245
|
}
|
254
246
|
};
|
255
247
|
```
|
256
248
|
|
257
|
-
:::info
|
258
|
-
[
|
259
|
-
|
249
|
+
:::info
|
250
|
+
[Example](https://github.com/web-infra-dev/modern-js-examples/tree/main/series/tutorials/runtime-api/model/effects)
|
260
251
|
:::
|
@@ -1,21 +1,22 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 7
|
3
|
-
title: Model
|
3
|
+
title: Model Communication
|
4
4
|
---
|
5
|
-
# Model 通信
|
6
5
|
|
7
|
-
|
6
|
+
# Model Communication
|
8
7
|
|
9
|
-
|
8
|
+
Model communication refers to communication between different Models, as well as communication between Effects and Actions within the same Model.
|
10
9
|
|
11
|
-
|
10
|
+
## Communication between Models
|
12
11
|
|
13
|
-
|
14
|
-
2. 在 Model 中监听其它 Model 变化。
|
12
|
+
Models are not isolated from each other and can communicate with each other. There are mainly two scenarios:
|
15
13
|
|
16
|
-
|
14
|
+
1. Accessing the State and Actions of other Models in the Model.
|
15
|
+
2. Listening to changes in other Models in the Model.
|
17
16
|
|
18
|
-
|
17
|
+
Here, we will transform the simple counter application in the [Quick Start](/guides/topic-detail/model/quick-start) section into a counter application that allows you to set the step frequency. By setting the step frequency, you can affect the magnitude of each counter increase.
|
18
|
+
|
19
|
+
We abstract two Models, `stepModel` (step frequency) and `counterModel` (counter):
|
19
20
|
|
20
21
|
```ts
|
21
22
|
import { model } from '@modern-js/runtime/model';
|
@@ -51,18 +52,17 @@ const counterModel = model('count').define((context, { use, onMount }) => {
|
|
51
52
|
export { stepModel, counterModel };
|
52
53
|
```
|
53
54
|
|
54
|
-
`stepModel`
|
55
|
-
|
56
|
-
`counterModel` 通过 `use` 函数加载 `stepModel`,拿到返回的 `subscribeStep` 函数,用来监听 `stepModel` 状态的变更。 `onMount` 是 Model 挂载完成后的钩子函数,`counterModel` 挂载完成后开始订阅 `stepModel` 状态的变更,打印出 `stepModel` 的最新值。
|
55
|
+
`stepModel` declares only one `state`, with an initial value of 1.
|
57
56
|
|
58
|
-
`counterModel`
|
57
|
+
`counterModel` loads `stepModel` with the `use` function, and retrieves the returned `subscribeStep` function to listen for changes to the `stepModel` state. `onMount` is a hook function that is executed after the Model is mounted. `counterModel` begins to subscribe to the state changes of `stepModel` after it has been mounted, and prints out the latest value of `stepModel`.
|
59
58
|
|
60
|
-
|
61
|
-
当需要访问其他 Model 的 State 时,必须要在当前 Actions 或 Effects 函数(本例中对应 `add` 函数 )真正执行的阶段调用 `use`,以保证获取的 State 是最新值。因此,我们虽然在 `define` 的回调函数中也调用了 `use(stepModel)`,但是我们并没有解构 `stepModel` 的 `state` 值,因为 `define` 的回调函数是在 Model 的挂载阶段执行的,这个时候获取到的 `stepModel` 的 `state` 可能和 `add` 执行时获取到的值是不同的。
|
59
|
+
`counterModel` accesses `stepModel` using the `use` function, and in the `add` function, the current value of `stepModel` (step frequency) can be obtained to perform increments using this value.
|
62
60
|
|
61
|
+
:::caution Note
|
62
|
+
When you need to access the state of another Model, you must call `use` during the actual execution phase of the current Actions or Effects function (in this example, the `add` function) to ensure that the obtained State is the latest value. Therefore, although we also call `use(stepModel)` in the callback function of `define`, we do not destructure the `state` value of `stepModel` because the callback function of `define` is executed during the mounting phase of the Model, and at this time, the `state` of `stepModel` obtained may be different from the value obtained when `add` is executed.
|
63
63
|
:::
|
64
64
|
|
65
|
-
|
65
|
+
Modify App.tsx:
|
66
66
|
|
67
67
|
```tsx
|
68
68
|
import { useModel } from '@modern-js/runtime/model';
|
@@ -87,45 +87,45 @@ export default function App() {
|
|
87
87
|
}
|
88
88
|
```
|
89
89
|
|
90
|
-
:::info
|
91
|
-
Modern.js
|
90
|
+
:::info Additional Information
|
91
|
+
Modern.js has enabled [auto-generate actions](./auto-actions.mdx) by default, so even though there are no Actions defined manually in the `stepModel`, the auto-generated `setState` can still be used.
|
92
92
|
|
93
93
|
:::
|
94
94
|
|
95
|
-
-
|
96
|
-
-
|
95
|
+
- Click **add step** to add steps.
|
96
|
+
- Click **add counter** to trigger the counter to increase.
|
97
97
|
|
98
|
-
|
98
|
+
The final effect is as follows:
|
99
99
|
|
100
100
|

|
101
101
|
|
102
|
-
:::info
|
103
|
-
-
|
104
|
-
-
|
102
|
+
:::info Additional Information
|
103
|
+
- Full example code for this section can be found [here](https://github.com/web-infra-dev/modern-js-examples/tree/main/series/tutorials/runtime-api/model/models-communication).
|
104
|
+
- For more information about the relevant API, please refer to: [model](/apis/app/runtime/model/model_#function-types).
|
105
105
|
|
106
106
|
:::
|
107
107
|
|
108
|
-
|
108
|
+
In the previous example of `counterModel`, we called `use` within the Actions function to get other Model objects. If we only need to call Actions of other Models, we can also use `use` to get the Actions of Models in the `define` callback function because Actions are functions and there is no issue of value expiration. For example:
|
109
109
|
|
110
110
|
```ts
|
111
111
|
const barModel = model('bar').define({
|
112
|
-
//
|
112
|
+
// ..
|
113
113
|
});
|
114
114
|
|
115
115
|
const fooModel = model('foo').define((context, utils) => {
|
116
|
-
//
|
116
|
+
// get barModel actions
|
117
117
|
const [, actions] = utils.use(barModel);
|
118
118
|
return {
|
119
|
-
//
|
119
|
+
// ...
|
120
120
|
effects: {
|
121
121
|
async loadA() {
|
122
|
-
//
|
123
|
-
//
|
122
|
+
// ...
|
123
|
+
// invoke barModel action
|
124
124
|
barModel.actionA();
|
125
125
|
},
|
126
126
|
async loadB() {
|
127
|
-
//
|
128
|
-
//
|
127
|
+
// ...
|
128
|
+
// invoke barModel action
|
129
129
|
barModel.actionB();
|
130
130
|
},
|
131
131
|
},
|
@@ -133,18 +133,18 @@ const fooModel = model('foo').define((context, utils) => {
|
|
133
133
|
});
|
134
134
|
```
|
135
135
|
|
136
|
-
|
136
|
+
Here, we no longer need to repeatedly get the `barModel` object in `loadA` and `loadB`, which simplifies the code logic.
|
137
137
|
|
138
|
-
## Model
|
138
|
+
## Communication within a Model
|
139
139
|
|
140
|
-
Model
|
140
|
+
Communication within a Model can also be divided into two main scenarios:
|
141
141
|
|
142
|
-
1. Effects
|
143
|
-
2. Actions
|
142
|
+
1. Effects functions call the Actions functions of the same Model or other Effects functions.
|
143
|
+
2. Actions functions call other Actions functions of the same Model.
|
144
144
|
|
145
|
-
|
145
|
+
In the [Managing Side Effects](/guides/topic-detail/model/manage-effects) section, we demonstrated how Effects functions call Actions functions.
|
146
146
|
|
147
|
-
|
147
|
+
Here we provide another example:
|
148
148
|
|
149
149
|
```ts
|
150
150
|
const fooModel = model('foo').define((context, { use, onMount }) => ({
|
@@ -162,13 +162,13 @@ const fooModel = model('foo').define((context, { use, onMount }) => ({
|
|
162
162
|
},
|
163
163
|
effects: {
|
164
164
|
async loadA() {
|
165
|
-
//
|
165
|
+
// get current Model actions
|
166
166
|
const [, actions] = use(fooModel);
|
167
167
|
const res = await mockFetchA();
|
168
168
|
actions.setA(res);
|
169
169
|
},
|
170
170
|
async loadB() {
|
171
|
-
//
|
171
|
+
// get current Model actions
|
172
172
|
const [, actions] = use(fooModel);
|
173
173
|
const res = await mockFetchB();
|
174
174
|
actions.setB(res);
|
@@ -177,16 +177,18 @@ const fooModel = model('foo').define((context, { use, onMount }) => ({
|
|
177
177
|
}));
|
178
178
|
```
|
179
179
|
|
180
|
-
|
180
|
+
In this example, the two Effects functions of `fooModel` need to call their own Actions functions. Here, we have called `use` once in each Effects function. Why can't we use `use` to get the Actions of the Model itself in the `define` callback function, as in the example of Model communication? This is because when calling `use` to get a Model, it first checks whether the Model has been mounted. If it has not been mounted, the mounting logic will be executed first.
|
181
|
+
|
182
|
+
The `define` callback function is executed during the mounting phase, so calling `use` to get the Model itself during the mounting phase will result in an infinite loop (which will throw an error in the actual execution process). Therefore, **you must not call `use` in the `define` callback function to get the Model itself**.
|
181
183
|
|
182
|
-
|
184
|
+
However, we can use the `onMount` hook function to get the Model itself through `use` after the Model has been mounted:
|
183
185
|
|
184
186
|
```ts
|
185
187
|
const fooModel = model('foo').define((context, { use, onMount }) => {
|
186
188
|
let actions;
|
187
189
|
|
188
190
|
onMount(() => {
|
189
|
-
// fooModel
|
191
|
+
// after fooModel mounted,get actions
|
190
192
|
[, actions] = use(fooModel);
|
191
193
|
});
|
192
194
|
|
@@ -217,4 +219,4 @@ const fooModel = model('foo').define((context, { use, onMount }) => {
|
|
217
219
|
});
|
218
220
|
```
|
219
221
|
|
220
|
-
|
222
|
+
In this way, we can also simplify the code.
|
@@ -1,16 +1,16 @@
|
|
1
1
|
---
|
2
2
|
sidebar_position: 8
|
3
|
-
title:
|
3
|
+
title: Performance Optimization
|
4
4
|
---
|
5
|
-
#
|
5
|
+
# Performance Optimization
|
6
6
|
|
7
|
-
Reduck
|
7
|
+
Reduck has already done a lot of performance optimization work internally, so performance issues generally do not need to be considered. However, when performance is more sensitive, or when encountering performance issues, you can consider more targeted performance optimization from the following three aspects.
|
8
8
|
|
9
|
-
##
|
9
|
+
## Splitting Models
|
10
10
|
|
11
|
-
|
11
|
+
When `useModel` returns the complete State object of the Model, any change in any part of the State will cause the component that calls `useModel` to be re-rendered.
|
12
12
|
|
13
|
-
|
13
|
+
For example:
|
14
14
|
|
15
15
|
```ts
|
16
16
|
const fooModel = model('foo').define({
|
@@ -35,7 +35,7 @@ function ComponentA() {
|
|
35
35
|
}
|
36
36
|
```
|
37
37
|
|
38
|
-
|
38
|
+
Although `ComponentA` only needs to use the `a` state, it will still be re-rendered when the `b` state changes. In this case, we can consider splitting `fooModel` into separate Models responsible for managing `a` and `b` respectively:
|
39
39
|
|
40
40
|
```ts
|
41
41
|
const fooModel = model('foo').define({
|
@@ -61,11 +61,11 @@ const barModel = model('bar').define({
|
|
61
61
|
});
|
62
62
|
```
|
63
63
|
|
64
|
-
##
|
64
|
+
## State Selection
|
65
65
|
|
66
|
-
`useModel`
|
66
|
+
`useModel` supports passing in a selector function to filter the returned State and Actions for the component. We can use a selector function to ensure that the State returned to the component is what the component needs directly, thus ensuring that the component is not re-rendered due to changes in other unrelated states.
|
67
67
|
|
68
|
-
|
68
|
+
For the same example above, we can use a selector function for performance optimization, the code is as follows:
|
69
69
|
|
70
70
|
```ts
|
71
71
|
const fooModel = model('foo').define({
|
@@ -91,11 +91,11 @@ function ComponentA() {
|
|
91
91
|
}
|
92
92
|
```
|
93
93
|
|
94
|
-
##
|
94
|
+
## Derivative State Caching
|
95
95
|
|
96
|
-
|
96
|
+
When a Model has `computed` property, the `computed` function will be executed every time `useModel` is called.
|
97
97
|
|
98
|
-
|
98
|
+
Consider the following code:
|
99
99
|
|
100
100
|
```ts
|
101
101
|
const barModel = model('bar').define({
|
@@ -104,7 +104,7 @@ const barModel = model('bar').define({
|
|
104
104
|
},
|
105
105
|
computed: {
|
106
106
|
combineA: [
|
107
|
-
fooModel, // fooModel
|
107
|
+
fooModel, // fooModel define as above
|
108
108
|
(state, fooState) => {
|
109
109
|
return state + fooState.a;
|
110
110
|
},
|
@@ -120,18 +120,17 @@ const barModel = model('bar').define({
|
|
120
120
|
function ComponentB() {
|
121
121
|
const [state, actions] = useModel(fooModel);
|
122
122
|
const [{ combineA }] = useModel(barModel);
|
123
|
-
//
|
123
|
+
// ...
|
124
124
|
}
|
125
125
|
```
|
126
|
+
Even if the `b` state of `fooModel` changes, the `combineA` function (more precisely, the last function type element of `combineA`) will still be called and executed when the component is re-rendered, although the derivative state `combineA` of `barModel` depends on `barModel` itself and the state `a` of `fooModel`.
|
126
127
|
|
127
|
-
|
128
|
-
|
129
|
-
一般情况下,`computed` 函数中的逻辑都是非常轻量的,但当 `computed` 函数逻辑比较复杂时,我们可以考虑对计算逻辑做缓存。例如,我们使用 [reselect](https://github.com/reduxjs/reselect) 对 `barModel` 的 `combineA` 做缓存:
|
128
|
+
In general, the logic in the `computed` function is usually very lightweight, but when the logic in the `computed` function is relatively complex, we can consider caching the calculation logic. For example, we can use [reselect](https://github.com/reduxjs/reselect) to cache `combineA` of `barModel`:
|
130
129
|
|
131
130
|
```ts
|
132
131
|
import 'createSelector' from 'reselect';
|
133
132
|
|
134
|
-
//
|
133
|
+
// create cache function
|
135
134
|
const selectCombineA = createSelector(
|
136
135
|
(state) => state.bar.value,
|
137
136
|
(state) => state.foo.a,
|
@@ -163,9 +162,9 @@ const barModel = model("bar").define({
|
|
163
162
|
});
|
164
163
|
```
|
165
164
|
|
166
|
-
|
167
|
-
|
168
|
-
:::info 补充信息
|
169
|
-
本节完整的[示例代码](https://github.com/modern-js-dev/modern-js-examples/tree/main/series/tutorials/runtime-api/model/performance-optimization)
|
165
|
+
We created a caching function `createSelector`, which only recalculates the value of `combineA` when the state of `barModel` changes or the state `a` of `fooModel` changes.
|
170
166
|
|
167
|
+
:::info Additional Information
|
168
|
+
You can find the complete example code of this section [here](https://github.com/web-infra-dev/modern-js-examples/tree/main/series/tutorials/runtime-api/model/performance-optimization).
|
171
169
|
:::
|
170
|
+
|