+
+
+
diff --git a/docs/tutorial/11-others/01-RSM.md b/docs/about/01-RSM.md
similarity index 98%
rename from docs/tutorial/11-others/01-RSM.md
rename to docs/about/01-RSM.md
index 854517793..63820c100 100644
--- a/docs/tutorial/11-others/01-RSM.md
+++ b/docs/about/01-RSM.md
@@ -1,61 +1,60 @@
----
-title: Request Scene Model (RSM)
-sidebar_position: 10
----
-
-## What is the request scene model
-
-The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions,
-
-1. When the request is made;
-2. Whether to display the request status;
-3. Do you need to retry the request on failure;
-4. How to process the response data;
-5. Do you need to encrypt the request parameters;
-6. Whether to cache the frequently used response data;
-7. How to operate data across pages;
-8. How to process requests in a weak or disconnected network environment;
-9. ...
-
-`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute.
-
-> CS interaction: generally refers to data interaction between all client types and servers
-
-## Request scene model
-
-![RSM](/img/rsm-en.png)
-
-## request timing
-
-Describe when a request needs to be made, implemented with `useHook` in `alova`.
-
-- Initialize display data, such as just entering a certain interface or sub-interface;
-- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.;
-- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure
-- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data;
-- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.;
-- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data;
-
-## Request Behavior
-
-Describes how to process the request, implemented as a Method abstraction in `alova`.
-
-- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time;
-- Cache high-frequency responses, multiple execution requests will use fresh data;
-- Multi-request serial and parallel;
-- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability;
-- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful;
-- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection;
-
-## request event
-
-Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`.
-
-## Response management
-
-`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified.
-
-- Remove the cached response data, which will be pulled from the server when the request is made again;
-- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages;
-- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages;
-- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data;
+---
+title: Request Scene Model (RSM)
+---
+
+## What is the request scene model
+
+The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions,
+
+1. When the request is made;
+2. Whether to display the request status;
+3. Do you need to retry the request on failure;
+4. How to process the response data;
+5. Do you need to encrypt the request parameters;
+6. Whether to cache the frequently used response data;
+7. How to operate data across pages;
+8. How to process requests in a weak or disconnected network environment;
+9. ...
+
+`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute.
+
+> CS interaction: generally refers to data interaction between all client types and servers
+
+## Request scene model
+
+![RSM](/img/rsm-en.png)
+
+## request timing
+
+Describe when a request needs to be made, implemented with `useHook` in `alova`.
+
+- Initialize display data, such as just entering a certain interface or sub-interface;
+- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.;
+- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure
+- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data;
+- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.;
+- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data;
+
+## Request Behavior
+
+Describes how to process the request, implemented as a Method abstraction in `alova`.
+
+- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time;
+- Cache high-frequency responses, multiple execution requests will use fresh data;
+- Multi-request serial and parallel;
+- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability;
+- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful;
+- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection;
+
+## request event
+
+Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`.
+
+## Response management
+
+`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified.
+
+- Remove the cached response data, which will be pulled from the server when the request is made again;
+- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages;
+- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages;
+- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data;
diff --git a/docs/about/02-comparison.md b/docs/about/02-comparison.md
new file mode 100644
index 000000000..60cc9b198
--- /dev/null
+++ b/docs/about/02-comparison.md
@@ -0,0 +1,92 @@
+---
+title: Compare with other libraries
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Compare with axios
+
+axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library.
+
+But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios.
+
+### alova provides automated request status management for axios
+
+When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook.
+
+
+
+
+```javascript
+// vue3 example
+const loading = ref(false);
+const data = ref({});
+const error = ref(null);
+const request = async () => {
+ try {
+ loading.value = true;
+ data.value = await axios.get('/xxx');
+ } catch (e) {
+ error.value = e;
+ }
+ loading.value = false;
+};
+mounted(request);
+```
+
+
+
+
+```javascript
+// Use axios as alova's request adapter
+const { loading, data, error } = useRequest(alova.Get('/xxx'));
+```
+
+
+
+
+### alova provides high-performance request strategies out of the box
+
+alova provides you with [multiple high-performance request strategy modules](/next/tutorial/client/strategy). You can use different modules according to different request scenarios, which axios does not have.
+
+### alova provides response data cache for axios
+
+alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure.
+
+### alova provides request sharing function for axios
+
+Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure.
+
+### alova provides data pre-fetching for axios
+
+Requesting the data to be used in advance can also greatly improve application fluency.
+
+### alova can manage request states
+
+You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication.
+
+## Compared with react-query and swr
+
+react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at.
+
+### alova has different goals
+
+In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved.
+
+### Method proxy design
+
+Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits:
+
+1. Unified usage without different usage depending on the platform or UI framework.
+2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration.
+3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs.
+4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache.
+
+### High flexibility
+
+Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios.
+
+### Lightweight
+
+alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality.
diff --git a/docs/about/03-qa.md b/docs/about/03-qa.md
new file mode 100644
index 000000000..1cd6b4cbc
--- /dev/null
+++ b/docs/about/03-qa.md
@@ -0,0 +1,133 @@
+---
+title: Question and Answer
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Why to create alova?
+
+Data requests have always been an indispensable and important part of applications. Since the birth of XMLHttpRequest, request schemes have emerged in endlessly. Client-side data interaction exploration has always focused on the simplicity of requests, such as `$.ajax`, `axios`, `fetch api` and Request tools such as `react-query`, the coding form continues to evolve from callback functions, Promise, to usehook. These js libraries have done a good job in making requests simple, but they only provide common functions, which means For different request scenarios such as sharing requests, paging requests, form submissions, uploading and downloading files, etc., developers still need to write complex codes themselves, which reduces development efficiency and performance cannot be guaranteed. As user experience becomes more and more important, In this era, application fluency has become more and more important.
+
+Additionally, the collaboration between client and server is also separated. Front-end engineers need to consult API documents and manually write API functions, and any changes of APIs need to actively notify front-end engineers, which will make your product more uncontrollable.
+
+**We think there is a simpler solution is that based on your request scenarios such as pagination, form submission, breakpoint resumption, etc., select the corresponding useHook, which will help you manage data and control when requests should be sent**. This allows developers to achieve more efficient Client-Server data interaction while writing little code.
+
+Additionally, alova has very flexible expansion capabilities to implement request strategies in different scenarios. You can also customize your own request scenarios. This part is in [Custom Chapter](/next/tutorial/advanced/custom).
+
+In order to cover more request scenarios, we also abstracted the request scenarios into [Request scene model(RSM)](/about/RSM), which explains alova's request strategy scheme well. In the future, alova will continue to carry forward our exploration of request strategies.
+
+## Alternative to the request libraries?
+
+alova is a request strategy library, which was originally created to provide specific request strategy solutions for different request scenarios, so as to achieve a smooth request experience more concisely and elegantly, such as `$.ajax`, `axios` and `fetch- api`, etc. provide good support for request sending and response receiving, they are an essential part of the [RSM](/about/RSM) process (request events), alova still needs to dependent on them to make requests, so we can Think of alova as an weaponry of the request library, making the request library more powerful.
+
+## Why binding UI framework?
+
+Decoupling a js library means using it in more scenarios. For example, axios can be used in nodejs, but it also means that developers need to write more template code, such as using useHooks to encapsulate axios. However, alova abandons more usage scenarios brought about by decoupling, and positions the Scope of usage in conjunction with the UI framework to use alova in the most streamlined way. This is for the benefit of developers and is prevalent in a UI framework. Sometimes, deep binding can provide developers with direct-use functions and improve the developer's experience without requiring too much template code.
+
+## How to use alova via cdn?
+
+
+
+
+```html
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
{{ error.message }}
+ responseData: {{ data }}
+
+
+
+
+```
+
+
+
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+:::tip
+
+svelte depends on the compilation tool and cannot be used directly through CDN. For details, see [svelte.dev](https://svelte.dev/)
+
+:::
+
+
+
+
+## What to pay attention to in React-Native?
+
+When using alova to develop React-Native applications, you can also use `alova/fetch`.
+
+But there are the following precautions:
+
+**Metro version**
+
+In alova's `package.json`, `exports` is used to define multiple export items, so you need to ensure these two points:
+
+1. Metro version is higher than 0.76.0
+
+2. Enable `resolver.unstable_enablePackageExports` in `metro.config.js`. [Click here for details](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental)
diff --git a/docs/tutorial/11-others/_category_.json b/docs/about/_category_.json
similarity index 92%
rename from docs/tutorial/11-others/_category_.json
rename to docs/about/_category_.json
index 07d61bd10..c562b25fb 100644
--- a/docs/tutorial/11-others/_category_.json
+++ b/docs/about/_category_.json
@@ -1,6 +1,6 @@
-{
- "label": "Others",
- "link": {
- "type": "generated-index"
- }
-}
+{
+ "label": "Others",
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/api/01-alova.md b/docs/api/01-alova.md
index 08201547c..5ab9dc7fc 100644
--- a/docs/api/01-alova.md
+++ b/docs/api/01-alova.md
@@ -1,6 +1,5 @@
---
title: alova instance
-sidebar_position: 10
---
## createAlova()
@@ -17,19 +16,18 @@ function createAlova(options?: AlovaOptions): Alova;
1. config: configuration parameters
-| Parameter name | Type | Description |
-| -------------- | --------------------------- | --------------------------------------------------------------------------------------------------------- |
-| baseURL | string | Base path, empty by default, [View details](/tutorial/getting-started/alova) |
-| statesHook | object | State management hook, optional, [View details](/tutorial/combine-framework) |
-| requestAdapter | object | Request adapter, required, [View details](/tutorial/custom/custom-http-adapter) |
-| timeout | number | Timeout time, no timeout by default, [View details](/tutorial/getting-started/alova) |
-| localCache | object | Local cache configuration, default GET has 5000ms cache, [View details](/tutorial/cache/mode) |
-| storageAdapter | object | Local storage adapter, default is `localStorage`, [View details](/tutorial/custom/custom-storage-adapter) |
-| beforeRequest | function | Before request hook, [View details](/tutorial/getting-started/global-interceptor) |
-| responded | object \| function | Request response hook, [View details](/tutorial/getting-started/global-interceptor) |
-| shareRequest | boolean | Share request, [View details](/tutorial/getting-started/alova) |
-| errorLogger | boolean\| null \| function | Error log, [View details](/tutorial/advanced/error-logger) |
-| cacheLogger | boolean \| null \| function | Cache log, [View details](/tutorial/advanced/cache-logger) |
+| Parameter name | Type | Description |
+| -------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------- |
+| baseURL | string | Base path, empty by default, [View details](/next/tutorial/getting-started/basic/alova) |
+| statesHook | object | State management hook, optional, [View details](/next/tutorial/getting-started/basic/combine-framework) |
+| requestAdapter | object | Request adapter, required, [View details](/next/tutorial/advanced/custom/http-adapter) |
+| timeout | number | Timeout time, no timeout by default, [View details](/next/tutorial/getting-started/basic/alova) |
+| cacheFor | object | Local cache configuration, default GET has 5000ms cache, [View details](/next/tutorial/cache/mode) |
+| storageAdapter | object | Local storage adapter, default is `localStorage`, [View details](/next/tutorial/advanced/custom/storage-adapter) |
+| beforeRequest | function | Before request hook, [View details](/next/tutorial/getting-started/basic/global-interceptor) |
+| responded | object \| function | Request response hook, [View details](/next/tutorial/getting-started/basic/global-interceptor) |
+| shareRequest | boolean | Share request, [View details](/next/tutorial/getting-started/basic/alova) |
+| cacheLogger | boolean \| null \| function | Cache log, [View details](/next/tutorial/advanced/in-depth/cache-logger) |
- **return**
@@ -39,11 +37,13 @@ Alova instance
```ts
import { createAlova } from 'alova';
+import adapterFetch from 'alova/fetch';
+import VueHook from 'alova/vue';
const alova = createAlova({
baseURL: 'https://example.com',
statesHook: VueHook,
- requestAdapter: GlobalFetch(),
+ requestAdapter: adapterFetch(),
timeout: 3000
// ...
});
@@ -51,7 +51,7 @@ const alova = createAlova({
## alova.id
-The alova instance id is used to distinguish different alova instances. It can accurately match the method instance of the specified alova in [method instance matcher](/tutorial/advanced/method-matcher).
+The alova instance id is used to distinguish different alova instances. It can accurately match the method instance of the specified alova in [method instance matcher](/next/tutorial/client/in-depth/method-matcher).
- **Type**: string
@@ -67,7 +67,7 @@ interface AlovaOptions {
requestAdapter: AlovaRequestAdapter;
baseURL?: string;
timeout?: number;
- localCache?: GlobalLocalCacheConfig;
+ cacheFor?: GlobalcacheForConfig;
storageAdapter?: AlovaStorageAdapter;
beforeRequest?: Function;
responded?: Function | ResponsedHandlerRecord;
@@ -108,18 +108,16 @@ interface Alova {
1. url: request address
2. config: configuration parameters
-| Parameter name | Type | Description |
-| -------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| headers | object | Request headers, [View details](/tutorial/getting-started/method) |
-| params | object | Request parameters, [View details](/tutorial/getting-started/method) |
-| name | string | method object name, in [updateState](/tutorial/advanced/update-across-components), [invalidateCache](/tutorial/cache/manually-invalidate), [setCache](/tutorial/cache/set-and-query), and [fetch function](/tutorial/advanced/use-fetcher), you can obtain the corresponding method instance by name or wildcard |
-| timeout | number | Request timeout, [View details](/tutorial/getting-started/method) |
-| localCache | LocalCacheConfig | Response cache time, [View details](/tutorial/cache/mode) |
-| hitSource | string | Hit the source method instance. When the source method instance request is successful, the cache of the current method instance will be invalidated. [View details](/tutorial/cache/auto-invalidate) |
-| enableDownload | boolean | Enable download progress information, [View details](/tutorial/combine-framework/download-upload-progress) |
-| enableUpload | boolean | Enable upload progress information, [View details](/tutorial/combine-framework/download-upload-progress) |
-| transformData | function | Transform response data, [View details](/tutorial/combine-framework/response) |
-| shareRequest | boolean | Request-level sharing request switch, [View details](/tutorial/getting-started/method) |
+| Parameter name | Type | Description |
+| -------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| headers | object | Request headers, [View details](/next/tutorial/getting-started/basic/method) |
+| params | object | Request parameters, [View details](/next/tutorial/getting-started/basic/method) |
+| name | string | method object name, in [updateState](/next/tutorial/client/in-depth/update-across-components), [invalidateCache](/next/tutorial/cache/manually-invalidate), [setCache](/next/tutorial/cache/set-and-query), and [fetch function](/next/tutorial/client/strategy/use-fetcher), you can obtain the corresponding method instance by name or wildcard |
+| timeout | number | Request timeout, [View details](/next/tutorial/getting-started/basic/method) |
+| cacheFor | cacheForConfig | Response cache time, [View details](/next/tutorial/cache/mode) |
+| hitSource | string | Hit the source method instance. When the source method instance request is successful, the cache of the current method instance will be invalidated. [View details](/next/tutorial/cache/auto-invalidate) |
+| transform | function | Transform response data, [View details](/next/tutorial/getting-started/basic/method) |
+| shareRequest | boolean | Request-level sharing request switch, [View details](/next/tutorial/getting-started/basic/method) |
> In addition to the configurable parameters above, other parameters supported by the request adapter are also supported.
@@ -146,7 +144,11 @@ Create a method instance for the POST request.
```ts
interface Alova {
- Post(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method;
+ Post(
+ url: string,
+ data?: object | FormData | string | null,
+ config?: AlovaMethodCreateConfig
+ ): Method;
}
```
@@ -184,7 +186,11 @@ Create a method instance for the DELETE request.
```ts
interface Alova {
- Delete(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method;
+ Delete(
+ url: string,
+ data?: object | FormData | string | null,
+ config?: AlovaMethodCreateConfig
+ ): Method;
}
```
@@ -220,7 +226,11 @@ Create a method instance for the PUT request.
```ts
interface Alova {
- Put(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method;
+ Put(
+ url: string,
+ data?: object | FormData | string | null,
+ config?: AlovaMethodCreateConfig
+ ): Method;
}
```
@@ -278,7 +288,11 @@ Create a method instance for the PATCH request.
```ts
interface Alova {
- Patch(url: string, data?: object | FormData | string | null, config?: AlovaMethodCreateConfig): Method;
+ Patch(
+ url: string,
+ data?: object | FormData | string | null,
+ config?: AlovaMethodCreateConfig
+ ): Method;
}
```
diff --git a/docs/api/02-method.md b/docs/api/02-method.md
index 4cda9714b..7c91ddf74 100644
--- a/docs/api/02-method.md
+++ b/docs/api/02-method.md
@@ -1,6 +1,5 @@
---
title: method instance
-sidebar_position: 20
---
A method instance corresponds to a request information description, which has the URL, request headers, request parameters of a request, as well as request behavior parameters such as response data processing and cache data processing. Through method instances, you can feel a unified usage experience in any js environment, and it can run normally with very few changes. In addition, method instances put request parameters and request behavior parameters together, making it easier for APIs management instead of spreading it across multiple code files.
@@ -52,7 +51,7 @@ interface MethodConstructor {
1. `type`: request type
2. `context`: alova instance
3. `url`: request url
-4. `config`: Configuration parameters, the type is the same as config parameter type of [alova.Get](/api/alova#alovaget)
+4. `config`: Configuration parameters, the type is the same as config parameter type of [alova.Get](/next/api/alova#alovaget)
5. `data`: request body data
- **Example**
@@ -97,7 +96,7 @@ const methodKey = getMethodKey(method);
## matchSnapshotMethod()
-Obtain the requested method instance snapshot using the matching method of [method instance matcher](/tutorial/advanced/method-matcher) and return the matching result.
+Obtain the requested method instance snapshot using the matching method of [method instance matcher](/next/tutorial/client/in-depth/method-matcher) and return the matching result.
- **type**
@@ -110,7 +109,10 @@ type MethodFilter =
filter?: MethodFilterHandler;
alova?: Alova;
};
-function matchSnapshotMethod(matcher: MethodFilter, matchAll?: boolean): Method[] | Method | undefined;
+function matchSnapshotMethod(
+ matcher: MethodFilter,
+ matchAll?: boolean
+): Method[] | Method | undefined;
```
- **Parameters**
@@ -151,7 +153,7 @@ interface Method {
## method.baseURL
-The base path of the request, inherited from [alova instance](/api/alova).
+The base path of the request, inherited from [alova instance](/next/api/alova).
- **type**
@@ -223,7 +225,7 @@ interface Method {
## method.meta
-The metadata of method is used to record request feature information, [View details](/tutorial/getting-started/method-metadata).
+The metadata of method is used to record request feature information, [View details](/next/tutorial/getting-started/basic/method-metadata).
- **type**
@@ -235,7 +237,7 @@ interface Method {
## method.config
-Configuration information when creating a method through `alova.Get/alova.Post` and other methods, [View details](/api/alova#alovaget).
+Configuration information when creating a method through `alova.Get/alova.Post` and other methods, [View details](/next/api/alova#alovaget).
- **type**
@@ -311,7 +313,10 @@ After `[v2.16.0]`, the method instance is a PromiseLike instance. You can direct
```ts
interface Method {
- then(onFulfilled?: (value: Response) => any, onRejected?: (reason: any) => any): Promise;
+ then(
+ onFulfilled?: (value: Response) => any,
+ onRejected?: (reason: any) => any
+ ): Promise;
}
```
diff --git a/docs/api/03-core-hooks.md b/docs/api/03-core-hooks.md
index 99327dd7a..78c3f5db4 100644
--- a/docs/api/03-core-hooks.md
+++ b/docs/api/03-core-hooks.md
@@ -1,268 +1,267 @@
----
-title: Core useHooks
-sidebar_position: 30
----
-
-## useRequest
-
-Represents the sending of a request. When executing useRequest, a request will be sent by default, and stateful request-related data will be created and maintained, such as `loading/data/error`, etc. It is the most commonly used method when obtaining initial data on the page. It also supports turning off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data.
-
-> Go to [Send Request](/tutorial/combine-framework/use-request) for details.
-
-### type
-
-```ts
-function useRequest(
- methodHandler: Method | (...args: any[]) => Method,
- config?: RequestHookConfig
-): UseHookReturnType;
-```
-
-### Parameters
-
-1. `methodHandler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned.
-2. `config`: hook configuration parameters.
-
-| Name | Description | Type | Default | Version |
-| ------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- |
-| immediate | Whether to initiate the request immediately | boolean | true | - |
-| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - |
-| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | - | - |
-| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | - |
-| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
-
-#### AlovaFrontMiddlewareContext
-
-| Name | Description | Type | Version |
-| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
-| method | The method object of the current request | Method | - |
-| cachedResponse | Hit cached data | any | - |
-| config | Current use hook configuration | Record\ | - |
-| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
-| frontStates | use hook front-end state collection, such as data, loading, error, etc. | [FrontRequestState](#frontrequeststate) | - |
-| send | Send request function | (...args: any[]) => Promise | - |
-| abort | abort function | () => void | - |
-| decorateSuccess | Decorate success callback function | (decorator: ( handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void, event: [AlovaSuccessEvent](#alovasuccessevent), index: number, length: number ) => void) => void | - |
-| decorateError | Decoration failure callback function | (decorator: ( handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void, event: [AlovaErrorEvent](#alovaerrorevent), index: number, length: number ) => void) => void | - |
-| decorateComplete | Decoration completion callback function | (decorator: ( handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent), index: number, length: number ) => void) => void | - |
-| update | Function to update the current use hook front-end state, more useful in react | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - |
-| controlLoading | Customize the loading state of the control. The call will no longer trigger changes in the loading state. When the incoming control is false, the control will be cancelled. | (control?: boolean) => void | - |
-
-#### AlovaGuardNext
-
-```typescript
-type AlovaGuardNext = (guardNextConfig?: {
- force?: boolean | (...args: any[]) => boolean;
- method?: Method;
-}): Promise;
-```
-
-#### FrontRequestState
-
-The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte
-
-| Name | Description | Type | Version |
-| ----------- | ----------------------------- | ------------------ | ------- |
-| loading | request loading status | boolean | - |
-| data | response data | any | - |
-| error | Request error information | Error \| undefined | - |
-| downloading | Download progress information | Object | - |
-| uploading | Upload progress information | Object | - |
-
-#### AlovaSuccessEvent
-
-| Name | Description | Type | Version |
-| --------- | ---------------------------------------------------------------------------------------- | ------- | ------- |
-| method | The method object of the current request | Method | - |
-| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
-| data | response data | any | - |
-| fromCache | Whether the response data comes from cache | boolean | - |
-
-#### AlovaErrorEvent
-
-| Name | Description | Type | Version |
-| -------- | ---------------------------------------------------------------------------------------- | ------ | ------- |
-| method | The method object of the current request | Method | - |
-| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
-| error | Response error instance | Error | - |
-
-#### AlovaCompleteEvent
-
-| Name | Description | Type | Version |
-| --------- | ---------------------------------------------------------------------------------------- | -------------------- | ------- |
-| method | The method object of the current request | Method | - |
-| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
-| status | Response status, success when successful, error when failure | 'success' \| 'error' | - |
-| data | response data, with value when successful | any | - |
-| fromCache | Whether the response data comes from the cache, a value if successful | boolean | - |
-| error | Response error instance, with value in case of failure | Error | - |
-
-### return value
-
-`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3 and the `Ref` type in react. It is a normal value and is of `Writable` type in svelte.
-
-#### Responsive data
-
-| Name | Description | Type | Version |
-| ----------- | ----------------------------- | ------------------ | ------- |
-| loading | request loading status | boolean | - |
-| data | response data | any | - |
-| error | Request error information | Error \| undefined | - |
-| downloading | Download progress information | Object | - |
-| uploading | Upload progress information | Object | - |
-
-#### Operation function
-
-| name | description | function parameters | return value | version |
-| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- |
-| send | Send request function | ...args: any[] | - | - |
-| abort | abort function | - | Promise | - |
-| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
-
-#### event
-
-| name | description | callback parameters | version |
-| ---------- | -------------------------------- | ------------------------------------------------ | ------- |
-| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - |
-| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - |
-| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - |
-
-## useWatcher
-
-Monitor the status and initiate a request after the status changes. In some scenarios that require re-requesting as the data changes, such as paging, data filtering, and fuzzy search.
-
-> Go to [State Change Request](/tutorial/combine-framework/use-watcher) for details.
-
-### type
-
-```typescript
-function useWatcher(
- handler: Method | (...args: any[]) => Method,
- watchingStates: State[],
- config?:WatcherHookConfig
-): UseHookReturnType;
-```
-
-### Parameters
-
-1. `handler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned.
-2. `config`: hook configuration parameters.
-
-| Name | Description | Type | Default | Version |
-| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- |
-| immediate | Whether to initiate the request immediately | boolean | true | - |
-| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - |
-| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false |
-| managedStates | Additional managed states, which can be updated via updateState | Record\ | - |
-| debounce | Request debounce time (milliseconds), when passing in the array, you can set the debounce time individually in the order of watchingStates | number \| number[] | - |
-| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
-| sendable | Whether to send a request when the monitored state changes | (methodInstance: AlovaEvent) => boolean | () => true | - |
-| abortLast | Whether to abort the last unresponsive request | boolean | true | - |
-
-### return value
-
-`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3 and ordinary value in react. In svelte it is Writable type.
-
-#### Responsive data
-
-| Name | Description | Type | Version |
-| ----------- | ----------------------------- | ------------------ | ------- |
-| loading | request loading status | boolean | - |
-| data | response data | any | - |
-| error | Request error information | Error \| undefined | - |
-| downloading | Download progress information | Object | - |
-| uploading | Upload progress information | Object | - |
-
-#### Operation function
-
-| name | description | function parameters | return value | version |
-| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- |
-| send | Send request function | ...args: any[] | Promise | - |
-| abort | abort function | - | - | - |
-| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
-
-#### event
-
-| name | description | callback parameters | version |
-| ---------- | -------------------------------- | ------------------------------------------------ | ------- |
-| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - |
-| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - |
-| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - |
-
-## useFetcher
-
-It is used to pull data through `useFetcher`, which is useful when preloading data and updating status across modules.
-
-> Go to [Data Fetching](/tutorial/advanced/use-fetcher) to view details.
-
-### type
-
-```typescript
-function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType;
-```
-
-### Parameters
-
-1. `config`: hook configuration parameters.
-
-| Name | Description | Type | Default | Version |
-| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------- |
-| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean | (...args: any[]) => boolean \| false | - |
-| middleware | Middleware function, [Understand alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
-
-#### AlovaFetcherMiddlewareContext
-
-| Name | Description | Type | Version |
-| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
-| method | The method object of the current request | Method | - |
-| cachedResponse | Hit cached data | any | - |
-| config | Current use hook configuration | Record\ | - |
-| fetchArgs | Parameters of the response processing callback, which are passed in by fetch of useFetcher | any[] | - |
-| fetchStates | use hook preload state collection, such as fetching, error, etc. | [FetchRequestState](#fetchrequeststate) | - |
-| fetch | Data preloading function | (method: Method, ...args: any[]) => Promise | - |
-| abort | abort function | () => void | - |
-| decorateSuccess | Decorate success callback function | (decorator: ( handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void, event: [AlovaSuccessEvent](#alovasuccessevent), index: number, length: number ) => void) => void | - |
-| decorateError | Decoration failure callback function | (decorator: ( handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void, event: [AlovaErrorEvent](#alovaerrorevent), index: number, length: number ) => void) => void | - |
-| decorateComplete | Decoration completion callback function | (decorator: ( handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent), index: number, length: number ) => void) => void | - |
-| update | Function to update the current use hook preloading state, more useful in react | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - |
-| controlFetching | After calling, the state of fetching will be customized and the change of fetching state will no longer be triggered internally. When the incoming control is false, the control will be canceled | (control?: boolean) => void | - |
-
-#### FetchRequestState
-
-The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte
-
-| Name | Description | Type | Version |
-| ----------- | ----------------------------- | ------------------ | ------- |
-| fetching | preloading request status | boolean | - |
-| error | Request error information | Error \| undefined | - |
-| downloading | Download progress information | Object | - |
-| uploading | Upload progress information | Object | - |
-
-### return value
-
-`UseFetchHookReturnType` contains request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3, ordinary value in react, and ordinary value in svelte. For Writable type.
-
-#### Responsive data
-
-| Name | Description | Type | Version |
-| ----------- | ----------------------------- | ------------------ | ------- |
-| fetching | preloading request status | boolean | - |
-| error | Request error information | Error \| undefined | - |
-| downloading | Download progress information | Object | - |
-| uploading | Upload progress information | Object | - |
-
-#### Operation function
-
-| name | description | function parameters | return value | version |
-| ------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------ | ------- |
-| fetch | Data preloading function | 1. method: preloaded Method instance 2. ...args: any[] | Promise | - |
-| abort | abort function | - | - | - |
-| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
-
-#### event
-
-| name | description | callback parameters | version |
-| ---------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- |
-| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovasuccessevent) | - |
-| onError | Request error event binding | event: [AlovaErrorEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovaerrorevent) | - |
-| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent) | - |
+---
+title: Core useHooks
+---
+
+## useRequest
+
+Represents the sending of a request. When executing useRequest, a request will be sent by default, and stateful request-related data will be created and maintained, such as `loading/data/error`, etc. It is the most commonly used method when obtaining initial data on the page. It also supports turning off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data.
+
+> Go to [Send Request](/next/tutorial/client/strategy/use-request) for details.
+
+### type
+
+```ts
+function useRequest(
+ methodHandler: Method | (...args: any[]) => Method,
+ config?: RequestHookConfig
+): UseHookReturnType;
+```
+
+### Parameters
+
+1. `methodHandler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned.
+2. `config`: hook configuration parameters.
+
+| Name | Description | Type | Default | Version |
+| ------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- |
+| immediate | Whether to initiate the request immediately | boolean | true | - |
+| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - |
+| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | - | - |
+| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | - |
+| middleware | Middleware function, [Understanding alova middleware](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
+
+#### AlovaFrontMiddlewareContext
+
+| Name | Description | Type | Version |
+| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
+| method | The method object of the current request | Method | - |
+| cachedResponse | Hit cached data | any | - |
+| config | Current use hook configuration | Record\ | - |
+| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
+| frontStates | use hook front-end state collection, such as data, loading, error, etc. | [FrontRequestState](#frontrequeststate) | - |
+| send | Send request function | (...args: any[]) => Promise | - |
+| abort | abort function | () => void | - |
+| decorateSuccess | Decorate success callback function | (decorator: ( handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void, event: [AlovaSuccessEvent](#alovasuccessevent), index: number, length: number ) => void) => void | - |
+| decorateError | Decoration failure callback function | (decorator: ( handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void, event: [AlovaErrorEvent](#alovaerrorevent), index: number, length: number ) => void) => void | - |
+| decorateComplete | Decoration completion callback function | (decorator: ( handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent), index: number, length: number ) => void) => void | - |
+| update | Function to update the current use hook front-end state, more useful in react | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - |
+| controlLoading | Customize the loading state of the control. The call will no longer trigger changes in the loading state. When the incoming control is false, the control will be cancelled. | (control?: boolean) => void | - |
+
+#### AlovaGuardNext
+
+```typescript
+type AlovaGuardNext = (guardNextConfig?: {
+ force?: boolean | (...args: any[]) => boolean;
+ method?: Method;
+}): Promise;
+```
+
+#### FrontRequestState
+
+The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| loading | request loading status | boolean | - |
+| data | response data | any | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+#### AlovaSuccessEvent
+
+| Name | Description | Type | Version |
+| --------- | ---------------------------------------------------------------------------------------- | ------- | ------- |
+| method | The method object of the current request | Method | - |
+| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
+| data | response data | any | - |
+| fromCache | Whether the response data comes from cache | boolean | - |
+
+#### AlovaErrorEvent
+
+| Name | Description | Type | Version |
+| -------- | ---------------------------------------------------------------------------------------- | ------ | ------- |
+| method | The method object of the current request | Method | - |
+| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
+| error | Response error instance | Error | - |
+
+#### AlovaCompleteEvent
+
+| Name | Description | Type | Version |
+| --------- | ---------------------------------------------------------------------------------------- | -------------------- | ------- |
+| method | The method object of the current request | Method | - |
+| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
+| status | Response status, success when successful, error when failure | 'success' \| 'error' | - |
+| data | response data, with value when successful | any | - |
+| fromCache | Whether the response data comes from the cache, a value if successful | boolean | - |
+| error | Response error instance, with value in case of failure | Error | - |
+
+### return value
+
+`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3 and the `Ref` type in react. It is a normal value and is of `Writable` type in svelte.
+
+#### Responsive data
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| loading | request loading status | boolean | - |
+| data | response data | any | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+#### Operation function
+
+| name | description | function parameters | return value | version |
+| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- |
+| send | Send request function | ...args: any[] | - | - |
+| abort | abort function | - | Promise | - |
+| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
+
+#### event
+
+| name | description | callback parameters | version |
+| ---------- | -------------------------------- | ------------------------------------------------ | ------- |
+| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - |
+| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - |
+| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - |
+
+## useWatcher
+
+Monitor the status and initiate a request after the status changes. In some scenarios that require re-requesting as the data changes, such as paging, data filtering, and fuzzy search.
+
+> Go to [State Change Request](/next/tutorial/client/strategy/use-watcher) for details.
+
+### type
+
+```typescript
+function useWatcher(
+ handler: Method | (...args: any[]) => Method,
+ watchingStates: State[],
+ config?:WatcherHookConfig
+): UseHookReturnType;
+```
+
+### Parameters
+
+1. `handler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned.
+2. `config`: hook configuration parameters.
+
+| Name | Description | Type | Default | Version |
+| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- |
+| immediate | Whether to initiate the request immediately | boolean | true | - |
+| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - |
+| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false |
+| managedStates | Additional managed states, which can be updated via updateState | Record\ | - |
+| debounce | Request debounce time (milliseconds), when passing in the array, you can set the debounce time individually in the order of watchingStates | number \| number[] | - |
+| middleware | Middleware function, [Understanding alova middleware](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
+| sendable | Whether to send a request when the monitored state changes | (methodInstance: AlovaEvent) => boolean | () => true | - |
+| abortLast | Whether to abort the last unresponsive request | boolean | true | - |
+
+### return value
+
+`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3 and ordinary value in react. In svelte it is Writable type.
+
+#### Responsive data
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| loading | request loading status | boolean | - |
+| data | response data | any | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+#### Operation function
+
+| name | description | function parameters | return value | version |
+| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- |
+| send | Send request function | ...args: any[] | Promise | - |
+| abort | abort function | - | - | - |
+| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
+
+#### event
+
+| name | description | callback parameters | version |
+| ---------- | -------------------------------- | ------------------------------------------------ | ------- |
+| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - |
+| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - |
+| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - |
+
+## useFetcher
+
+It is used to pull data through `useFetcher`, which is useful when preloading data and updating status across modules.
+
+> Go to [Data Fetching](/next/tutorial/client/strategy/use-fetcher) to view details.
+
+### type
+
+```typescript
+function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType;
+```
+
+### Parameters
+
+1. `config`: hook configuration parameters.
+
+| Name | Description | Type | Default | Version |
+| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------- |
+| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean | (...args: any[]) => boolean \| false | - |
+| middleware | Middleware function, [Understand alova middleware](/next/tutorial/client/in-depth/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
+
+#### AlovaFetcherMiddlewareContext
+
+| Name | Description | Type | Version |
+| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
+| method | The method object of the current request | Method | - |
+| cachedResponse | Hit cached data | any | - |
+| config | Current use hook configuration | Record\ | - |
+| fetchArgs | Parameters of the response processing callback, which are passed in by fetch of useFetcher | any[] | - |
+| fetchStates | use hook preload state collection, such as fetching, error, etc. | [FetchRequestState](#fetchrequeststate) | - |
+| fetch | Data preloading function | (method: Method, ...args: any[]) => Promise | - |
+| abort | abort function | () => void | - |
+| decorateSuccess | Decorate success callback function | (decorator: ( handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void, event: [AlovaSuccessEvent](#alovasuccessevent), index: number, length: number ) => void) => void | - |
+| decorateError | Decoration failure callback function | (decorator: ( handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void, event: [AlovaErrorEvent](#alovaerrorevent), index: number, length: number ) => void) => void | - |
+| decorateComplete | Decoration completion callback function | (decorator: ( handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent), index: number, length: number ) => void) => void | - |
+| update | Function to update the current use hook preloading state, more useful in react | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - |
+| controlFetching | After calling, the state of fetching will be customized and the change of fetching state will no longer be triggered internally. When the incoming control is false, the control will be canceled | (control?: boolean) => void | - |
+
+#### FetchRequestState
+
+The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| fetching | preloading request status | boolean | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+### return value
+
+`UseFetchHookReturnType` contains request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3, ordinary value in react, and ordinary value in svelte. For Writable type.
+
+#### Responsive data
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| fetching | preloading request status | boolean | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+#### Operation function
+
+| name | description | function parameters | return value | version |
+| ------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------ | ------- |
+| fetch | Data preloading function | 1. method: preloaded Method instance 2. ...args: any[] | Promise | - |
+| abort | abort function | - | - | - |
+| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
+
+#### event
+
+| name | description | callback parameters | version |
+| ---------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- |
+| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovasuccessevent) | - |
+| onError | Request error event binding | event: [AlovaErrorEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovaerrorevent) | - |
+| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent) | - |
diff --git a/docs/api/04-cache.md b/docs/api/04-cache.md
index 86414f8e0..9f2747649 100644
--- a/docs/api/04-cache.md
+++ b/docs/api/04-cache.md
@@ -1,13 +1,12 @@
---
title: Cache operation
-sidebar_position: 40
---
## invalidateCache()
Active cache invalidation.
-> Go to [Manually invalidate cache](/tutorial/cache/manually-invalidate) for details.
+> Go to [Manually invalidate cache](/next/tutorial/cache/manually-invalidate) for details.
- **type**
@@ -25,7 +24,7 @@ function invalidateCache(matcher?: Method | Method[] | MethodFilter): void;
- **Parameters**
-1. `matcher`: Cache invalid matcher, the value is a method instance or array, or it can be set to [method instance matcher](/tutorial/advanced/method-matcher).
+1. `matcher`: Cache invalid matcher, the value is a method instance or array, or it can be set to [method instance matcher](/next/tutorial/client/in-depth/method-matcher).
- **return**
@@ -48,7 +47,7 @@ invalidateCache({
Set up response caching.
-> Go to [Cache Update and Query](/tutorial/cache/set-and-query) for details.
+> Go to [Cache Update and Query](/next/tutorial/cache/set-and-query) for details.
- **type**
@@ -69,7 +68,7 @@ function setCache(
- **Parameters**
-1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/tutorial/advanced/method-matcher), which will match all matching The method instance of the condition sets the cached data.
+1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/next/tutorial/client/in-depth/method-matcher), which will match all matching The method instance of the condition sets the cached data.
2. `dataOrUpdater`: Cache data or update function. If it is a function, it needs to return new cached data. If it returns `undefined` or does not return, the update will be cancelled.
- **return**
@@ -96,7 +95,7 @@ setCache(
Query cache.
-> Go to [Cache Update and Query](/tutorial/cache/set-and-query) for details.
+> Go to [Cache Update and Query](/next/tutorial/cache/set-and-query) for details.
- **type**
@@ -114,7 +113,7 @@ function queryCache(matcher?: Method | MethodFilter): R | undefined;
- **Parameters**
-1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/tutorial/advanced/method-matcher), which will meet the conditions. The first method instance queries cached data.
+1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/next/tutorial/client/in-depth/method-matcher), which will meet the conditions. The first method instance queries cached data.
- **return**
diff --git a/docs/api/05-states.md b/docs/api/05-states.md
index a32f22de5..ac749c754 100644
--- a/docs/api/05-states.md
+++ b/docs/api/05-states.md
@@ -1,6 +1,5 @@
---
title: Response states operation
-sidebar_position: 50
---
## updateState
@@ -14,11 +13,11 @@ By default, `updateState` will look for the response state created by alova's us
This problem often occurs when updating status across pages, because what we tend to overlook when the page jumps is that the previous page has been destroyed by default. Therefore, if you want to update status across pages, here are two suggestions :
1. Persist the page components to ensure that the updated status can still be found;
-2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect.
+2. Use [setCache](/next/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect.
-> Go to [Cross-page/module update response states](/tutorial/advanced/update-across-components) for details.
+> Go to [Cross-page/module update response states](/next/tutorial/client/in-depth/update-across-components) for details.
-> To use updateState to manage extra states, please refer to [Extra State Management](/tutorial/advanced/manage-extra-states).
+> To use updateState to manage extra states, please refer to [Extra State Management](/next/tutorial/client/in-depth/manage-extra-states).
- **type**
@@ -40,7 +39,7 @@ function updateState(
- **Parameters**
-- `matcher`: The value is method instance, method name string, method name regular expression, it can also be set to [method instance matcher](/tutorial/advanced/method-matcher), if it matches the qualified method, `handleUpdate` will be called.
+- `matcher`: The value is method instance, method name string, method name regular expression, it can also be set to [method instance matcher](/next/tutorial/client/in-depth/method-matcher), if it matches the qualified method, `handleUpdate` will be called.
- `handleUpdate`: update function or update function collection. If it is a function collection, the corresponding update function on the collection will be called and the return value will be used as the update result.
- `options`: optional options.
diff --git a/docs/api/06-global-config.md b/docs/api/06-global-config.md
index d359d10c6..a3feb3094 100644
--- a/docs/api/06-global-config.md
+++ b/docs/api/06-global-config.md
@@ -1,36 +1,35 @@
----
-title: Global configuration
-sidebar_position: 60
----
-
-## globalConfig()
-
-Global configuration.
-
-- **type**
-
-```ts
-function globalConfig(config: AlovaGlobalConfig): void;
-```
-
-- **Parameters**
-
-1. config: configuration
-
-| Parameter name | Type | Description |
-| -------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
-| limitSnapshots | number | method snapshot number limit, set to 0 to disable saving snapshots. After closing, the method snapshot matcher will be unavailable |
-
-- **return**
-
-none
-
-- **Example**
-
-```ts
-import { globalConfig } from 'alova';
-
-globalConfig({
- limitSnapshots: 10
-});
-```
+---
+title: Global configuration
+---
+
+## globalConfig()
+
+Global configuration.
+
+- **type**
+
+```ts
+function globalConfig(config: AlovaGlobalConfig): void;
+```
+
+- **Parameters**
+
+1. config: configuration
+
+| Parameter name | Type | Description |
+| -------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
+| limitSnapshots | number | method snapshot number limit, set to 0 to disable saving snapshots. After closing, the method snapshot matcher will be unavailable |
+
+- **return**
+
+none
+
+- **Example**
+
+```ts
+import { globalConfig } from 'alova';
+
+globalConfig({
+ limitSnapshots: 10
+});
+```
diff --git a/docs/api/_category_.json b/docs/api/_category_.json
index 4b4861e39..de7065684 100644
--- a/docs/api/_category_.json
+++ b/docs/api/_category_.json
@@ -1,6 +1,6 @@
-{
- "label": "API",
- "link": {
- "type": "generated-index"
- }
-}
+{
+ "label": "API",
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/contributing/01-overview.md b/docs/contributing/01-overview.md
index 4a74bfb3d..e5dab7393 100644
--- a/docs/contributing/01-overview.md
+++ b/docs/contributing/01-overview.md
@@ -1,175 +1,174 @@
----
-title: Contribution Guidelines
-sidebar_position: 10
----
-
-# alova Contribution Guidelines
-
-Hello, I'm glad to meet you here. This is a detailed alova contribution guidelines, which includes detailed guidance on contributing to all aspects of alova. Please continue reading.
-
-## Preface
-
-In the past period of time, we have received active participation information from developers from all over the world in Github issues and Github Discussion, which means that alova is being loved by more and more developers. Even so, alova is still a rookie, and it still has a long way to go.
-
-**We expect to make alova a common project for everyone who is willing to participate, and we encourage everyone to become a contributor to the alova community with an open and inclusive attitude. Moreover, we believe that contributing to alova is not limited to code contributions, but participating in any activities that are beneficial to the development of alova is considered to contribute to alova.** Participating in contributions now can win you more effective contribution opportunities, and it allows you to contribute to the world Even if you are a junior developer, as long as the idea is in line with [alova's mission and design principles](#alova-mission-and-design-principles), please generously participate!
-
-> There is a [Code of conduct](./code-of-conduct) here, please refer to it.
-
-## Contribution directory
-
-Here are 13 places where you can contribute, but not limited to these, you can choose the part you want to participate in, and link to the corresponding location for detailed reading:
-
-- [use alova in your project](#use-alova-in-your-project)
-- [star alova](#star-alova)
-- [report bug](#report-bug)
-- [Propose new feature ideas](#propose-new-feature-ideas)
-- [Pull Request](#pull-request)
-- [Create an adapter or strategy library based on alova](#create-an-adapter-or-strategy-library-based-on-alova)
-- [Participate in community/PR review](#participate-in-community-pr-review)
-- [Publish and disseminate information about alova](#publish-and-disseminate-information-about-alova)
-- [Share experience](#share-experience)
-- [Collaboration](#collaboration)
-- [Donation](#donation)
-- [Correct or add docs](#correct-or-add-docs)
-- [Translate docs](#Translate-docs)
-
-## alova mission and design principles
-
-### alova Mission
-
-alova's mission gives it a clear development direction, which clearly defines what alova should do.
-
-alova is a lightweight request strategy library, **Its mission is to enable developers to achieve more efficient Client-Server data interaction by less codes**.
-
-For developers, alova provides them with a simple API and out-of-the-box advanced request functions, as well as various simple and high-performance request strategy modules. For application users, they can enjoy the benefits of alova The smooth experience brought by high-performance data interaction, therefore, alova has the following features:
-
-1. The api design is similar to axios, allowing users to learn at a lower cost;
-2. Deep binding of the UI framework, greatly improving the benefits of developers;
-3. Out-of-the-box advanced functions to avoid repeated packaging, such as request sharing, request caching, etc., to reduce developers' repeated packaging;
-4. The platform-independent coding way, and it can perfectly migrate in different platform;
-5. High scalability design, which can encapsulate high-reusability and high-performance business-related request strategies;
-6. Highly aggregated and low-coupling method design improves API code maintainability;
-
-### alova design principles
-
-The design principles points out how it should be designed, the following is the core design concept of alova.
-
-1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ;
-2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter;
-3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transformData`, `localCache`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests.
-4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration
-
-> The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business.
-
-## Select the contribution point you are interested in
-
-### Use alova in your project
-
-We believe that your use of alova in the project is also a contributor to alova, which is also telling people that alova is a trustworthy open source project, please open [this issue](https://github.com/alovajs/alova/issues/165), which may give you the opportunity to display your project on the alova official website.
-
-### Star alova
-
-Although this may be considered insignificant, it represents your recognition of alova, and every star is very important to alova. star alova in the right top of [alova's Github repository](https://github.com/alovajs/alova), which is important to us.
-
-### Report bug
-
-Please move to [Github new issue](https://github.com/alovajs/alova/issues/new/choose) and select the corresponding template to submit. Detailed instructions will be displayed in the submitted issue.
-
-**PLEASE NOTE:** If you want to ask questions about alova, please go to [Github Discussion](https://github.com/alovajs/alova/discussions) to create a question. That would be closed immediately when ask a question in issues.
-
-### Propose new feature ideas
-
-In order for alova to achieve its value and goals, before submitting a new feature idea, please carefully read [alova mission and design principles](#alova-mission-and-design-principles), and ensure that your new idea is in line with alova's mission and design philosophy design concept.
-
-Then, please submit it in [🚀 New Feature Proposal](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_en.yml), the detailed description will be Displayed when submitting an issue.
-
-### Pull Request
-
-You can contribute the following 3 aspects of code through pull request. If you are a new partner who is interested in participating, all `good first issue` issues are listed in [Github contribution list](https://github.com/alovajs/alova/contribute), it is used to Tell new partners who are interested in contributing that this is a good start.
-
-#### Bug fix
-
-Issues marked as [`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed) in Github issues are all confirmed bugs, you can choose freely.
-
-If you encounter a bug yourself, please also [report a bug](#report-bug) first to ensure that the bug is confirmed to avoid invalid pull requests.
-
-#### New feature development
-
-Issues marked as [`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed) in Github issues are new features that have been confirmed, you You can choose freely.
-
-If you have an idea for adding a new feature, please also [submit an issue of a new feature idea](#propose-new-feature-ideas) to ensure that the idea is confirmed to avoid invalid pull requests.
-
-#### Project configuration
-
-If you are very good at project configuration and find deficiencies in the alova project, such as incomplete configuration, too old configuration version, insufficient automation (including project development automation and Github warehouse management automation), you can also press [New Feature development](#new-feature-development) process to contribute.
-
-:::warning IMPORTANT
-
-1. Please read the [Developing Guidelines](./developing-guidelines) carefully before developing, it can guide you step by step on how to contribute code.
-2. When you identify an issue that needs to be resolved, please make sure that it has not been flagged by someone else's pull request, which means that it has been pre-occupied.
-
-:::
-
-### Create an adapter or strategy library based on alova
-
-alova provides high-extensibility features, and you can write your own js library based on it.
-
-#### Custom Adapter
-
-Customize various adapters to meet the operating requirements in different environments. The following directions are available for reference:
-
-1. Customize statesHook, which can be executed under different UI frameworks, such as `solid/qwik`, currently supports `react/vue/svelte`, please read [Custom statesHook](/tutorial/custom/custom-stateshook);
-2. Customize the request adapter, so that alova can cooperate with more request schemes, such as `GraphQL/SSE`, etc.;
-3. Customize the storage adapter to meet the storage requirements of different environments, such as `react-native`;
-4. Any combination of the above, such as the official [uniapp adapter](https://github.com/alovajs/adapter-uniapp), which includes request adapters and storage adapters.
-
-#### Custom request strategy
-
-Request strategies can help developers write high-performance functions more efficiently. Although the official [@alova/scene](/tutorial/strategy) provides some common request strategies, it is not enough It is a good choice to customize your own reusable request strategy based on alova to meet the business scenarios related to various requests of developers, and you can also publish them on npm for everyone to use.
-
-:::tip Submit your project
-
-If you have written an alova-based js library, please submit your project in [this issue](https://github.com/alovajs/alova/issues/165), which will allow your project to be displayed on the alova official website Opportunity.
-
-:::
-
-### Participate in community/PR review
-
-If you are interested in technical communication, then it may be more suitable for you to participate in more community communication. You can participate in the discussion of bugs and new features in Github issues, or in [Github Discussion](https://github.com/alovajs/alova/discussions), [Discord](https://discord.gg/S47QGJgkVb) or [QQ channel](https://pd.qq.com/s/1cdjx0nnw) to answer questions for others, which allows you to communicate with people from all over the world, which is a very interesting thing.
-
-At the same time, you can also participate in PR review in [pull request](https://github.com/alovajs/alova/pulls), which is also a topic of communication.
-
-### Publish and disseminate information about alova
-
-You can publish or repost any information that is beneficial to the development of alova on any social platform, short video platform, or technology sharing platform, which will help increase the influence of alova. We will filter out relevant articles or videos and display them on the alova official website. Here are some good articles:
-
-- [It’s time to replace your axios](https://medium.com/@huzhen555/its-time-to-replace-your-axios-12c014833b04)
-- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9)
-
-### Share experience
-
-If you have alova experience worth sharing, or better practice cases, you can share it in [Github Discussion's Practices channel](https://github.com/alovajs/alova/discussions/categories/practices), better The sharing of will also be displayed in the official documentation.
-
-### Collaboration
-
-We welcome project collaboration with any organization or individual, which can help us expand alova's influence and accelerate the development of the project. If you have any suggestions or intentions for cooperation, please contact us via email **hujou555@gmail.com**.
-
-### Donation
-
-You can donate to the project through the following 3 ways. Please go to the donation page to view the donation privileges.
-
-1. [Github sponsors](https://github.com/sponsors/alovajs)
-2. [OpenCollective](https://opencollective.com/alova)
-3. [afdian](https://afdian.net/a/huzhen555)
-
-### Correct or add docs
-
-If you need to add new documentation content, or find errors in alova's documentation, such as wrong examples, wrong words, incorrect descriptions, or unmentioned content, you can [create a new document repository issue](https://github.com/alovajs/alovajs.github.io/issues/new), or [new document warehouse pull request](https://github.com/alovajs/alovajs.github.io/fork) directly modify the error, which should be the better option, and we welcome any suggestions or contributions to improve the documentation.
-
-### Translate Documentation
-
-If you are proficient in different languages, you are welcome to translate the alova documentation, which will help expand the use and audience of alova.
-
-## Become a core team member
-
-Check [HERE](./become-core-member) for details
+---
+title: Contribution Guidelines
+---
+
+# alova Contribution Guidelines
+
+Hello, I'm glad to meet you here. This is a detailed alova contribution guidelines, which includes detailed guidance on contributing to all aspects of alova. Please continue reading.
+
+## Preface
+
+In the past period of time, we have received active participation information from developers from all over the world in Github issues and Github Discussion, which means that alova is being loved by more and more developers. Even so, alova is still a rookie, and it still has a long way to go.
+
+**We expect to make alova a common project for everyone who is willing to participate, and we encourage everyone to become a contributor to the alova community with an open and inclusive attitude. Moreover, we believe that contributing to alova is not limited to code contributions, but participating in any activities that are beneficial to the development of alova is considered to contribute to alova.** Participating in contributions now can win you more effective contribution opportunities, and it allows you to contribute to the world Even if you are a junior developer, as long as the idea is in line with [alova's mission and design principles](#alova-mission-and-design-principles), please generously participate!
+
+> There is a [Code of conduct](./code-of-conduct) here, please refer to it.
+
+## Contribution directory
+
+Here are 13 places where you can contribute, but not limited to these, you can choose the part you want to participate in, and link to the corresponding location for detailed reading:
+
+- [use alova in your project](#use-alova-in-your-project)
+- [star alova](#star-alova)
+- [report bug](#report-bug)
+- [Propose new feature ideas](#propose-new-feature-ideas)
+- [Pull Request](#pull-request)
+- [Create an adapter or strategy library based on alova](#create-an-adapter-or-strategy-library-based-on-alova)
+- [Participate in community/PR review](#participate-in-community-pr-review)
+- [Publish and disseminate information about alova](#publish-and-disseminate-information-about-alova)
+- [Share experience](#share-experience)
+- [Collaboration](#collaboration)
+- [Donation](#donation)
+- [Correct or add docs](#correct-or-add-docs)
+- [Translate docs](#Translate-docs)
+
+## alova mission and design principles
+
+### alova Mission
+
+alova's mission gives it a clear development direction, which clearly defines what alova should do.
+
+alova is a lightweight request strategy library, **Its mission is to enable developers to achieve more efficient Client-Server data interaction by less codes**.
+
+For developers, alova provides them with a simple API and out-of-the-box advanced request functions, as well as various simple and high-performance request strategy modules. For application users, they can enjoy the benefits of alova The smooth experience brought by high-performance data interaction, therefore, alova has the following features:
+
+1. The api design is similar to axios, allowing users to learn at a lower cost;
+2. Deep binding of the UI framework, greatly improving the benefits of developers;
+3. Out-of-the-box advanced functions to avoid repeated packaging, such as request sharing, request caching, etc., to reduce developers' repeated packaging;
+4. The platform-independent coding way, and it can perfectly migrate in different platform;
+5. High scalability design, which can encapsulate high-reusability and high-performance business-related request strategies;
+6. Highly aggregated and low-coupling method design improves API code maintainability;
+
+### alova design principles
+
+The design principles points out how it should be designed, the following is the core design concept of alova.
+
+1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ;
+2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter;
+3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transform`, `cacheFor`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests.
+4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration
+
+> The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business.
+
+## Select the contribution point you are interested in
+
+### Use alova in your project
+
+We believe that your use of alova in the project is also a contributor to alova, which is also telling people that alova is a trustworthy open source project, please open [this issue](https://github.com/alovajs/alova/issues/165), which may give you the opportunity to display your project on the alova official website.
+
+### Star alova
+
+Although this may be considered insignificant, it represents your recognition of alova, and every star is very important to alova. star alova in the right top of [alova's Github repository](https://github.com/alovajs/alova), which is important to us.
+
+### Report bug
+
+Please move to [Github new issue](https://github.com/alovajs/alova/issues/new/choose) and select the corresponding template to submit. Detailed instructions will be displayed in the submitted issue.
+
+**PLEASE NOTE:** If you want to ask questions about alova, please go to [Github Discussion](https://github.com/alovajs/alova/discussions) to create a question. That would be closed immediately when ask a question in issues.
+
+### Propose new feature ideas
+
+In order for alova to achieve its value and goals, before submitting a new feature idea, please carefully read [alova mission and design principles](#alova-mission-and-design-principles), and ensure that your new idea is in line with alova's mission and design philosophy design concept.
+
+Then, please submit it in [🚀 New Feature Proposal](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_en.yml), the detailed description will be Displayed when submitting an issue.
+
+### Pull Request
+
+You can contribute the following 3 aspects of code through pull request. If you are a new partner who is interested in participating, all `good first issue` issues are listed in [Github contribution list](https://github.com/alovajs/alova/contribute), it is used to Tell new partners who are interested in contributing that this is a good start.
+
+#### Bug fix
+
+Issues marked as [`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed) in Github issues are all confirmed bugs, you can choose freely.
+
+If you encounter a bug yourself, please also [report a bug](#report-bug) first to ensure that the bug is confirmed to avoid invalid pull requests.
+
+#### New feature development
+
+Issues marked as [`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed) in Github issues are new features that have been confirmed, you You can choose freely.
+
+If you have an idea for adding a new feature, please also [submit an issue of a new feature idea](#propose-new-feature-ideas) to ensure that the idea is confirmed to avoid invalid pull requests.
+
+#### Project configuration
+
+If you are very good at project configuration and find deficiencies in the alova project, such as incomplete configuration, too old configuration version, insufficient automation (including project development automation and Github warehouse management automation), you can also press [New Feature development](#new-feature-development) process to contribute.
+
+:::warning IMPORTANT
+
+1. Please read the [Developing Guidelines](./developing-guidelines) carefully before developing, it can guide you step by step on how to contribute code.
+2. When you identify an issue that needs to be resolved, please make sure that it has not been flagged by someone else's pull request, which means that it has been pre-occupied.
+
+:::
+
+### Create an adapter or strategy library based on alova
+
+alova provides high-extensibility features, and you can write your own js library based on it.
+
+#### Custom Adapter
+
+Customize various adapters to meet the operating requirements in different environments. The following directions are available for reference:
+
+1. Customize statesHook, which can be executed under different UI frameworks, such as `solid/qwik`, currently supports `react/vue/svelte`, please read [Custom statesHook](/next/tutorial/advanced/custom/stateshook);
+2. Customize the request adapter, so that alova can cooperate with more request schemes, such as `GraphQL/SSE`, etc.;
+3. Customize the storage adapter to meet the storage requirements of different environments, such as `react-native`;
+4. Any combination of the above, such as the official [uniapp adapter](https://github.com/alovajs/adapter-uniapp), which includes request adapters and storage adapters.
+
+#### Custom request strategy
+
+Request strategies can help developers write high-performance functions more efficiently. Although the official [@alova/scene](/next/tutorial/client/strategy) provides some common request strategies, it is not enough It is a good choice to customize your own reusable request strategy based on alova to meet the business scenarios related to various requests of developers, and you can also publish them on npm for everyone to use.
+
+:::tip Submit your project
+
+If you have written an alova-based js library, please submit your project in [this issue](https://github.com/alovajs/alova/issues/165), which will allow your project to be displayed on the alova official website Opportunity.
+
+:::
+
+### Participate in community/PR review
+
+If you are interested in technical communication, then it may be more suitable for you to participate in more community communication. You can participate in the discussion of bugs and new features in Github issues, or in [Github Discussion](https://github.com/alovajs/alova/discussions), [Discord](https://discord.gg/S47QGJgkVb) or [QQ channel](https://pd.qq.com/s/1cdjx0nnw) to answer questions for others, which allows you to communicate with people from all over the world, which is a very interesting thing.
+
+At the same time, you can also participate in PR review in [pull request](https://github.com/alovajs/alova/pulls), which is also a topic of communication.
+
+### Publish and disseminate information about alova
+
+You can publish or repost any information that is beneficial to the development of alova on any social platform, short video platform, or technology sharing platform, which will help increase the influence of alova. We will filter out relevant articles or videos and display them on the alova official website. Here are some good articles:
+
+- [It’s time to replace your axios](https://medium.com/@huzhen555/its-time-to-replace-your-axios-12c014833b04)
+- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9)
+
+### Share experience
+
+If you have alova experience worth sharing, or better practice cases, you can share it in [Github Discussion's Practices channel](https://github.com/alovajs/alova/discussions/categories/practices), better The sharing of will also be displayed in the official documentation.
+
+### Collaboration
+
+We welcome project collaboration with any organization or individual, which can help us expand alova's influence and accelerate the development of the project. If you have any suggestions or intentions for cooperation, please contact us via email **hujou555@gmail.com**.
+
+### Donation
+
+You can donate to the project through the following 3 ways. Please go to the donation page to view the donation privileges.
+
+1. [Github sponsors](https://github.com/sponsors/alovajs)
+2. [OpenCollective](https://opencollective.com/alova)
+3. [afdian](https://afdian.net/a/huzhen555)
+
+### Correct or add docs
+
+If you need to add new documentation content, or find errors in alova's documentation, such as wrong examples, wrong words, incorrect descriptions, or unmentioned content, you can [create a new document repository issue](https://github.com/alovajs/alovajs.github.io/issues/new), or [new document warehouse pull request](https://github.com/alovajs/alovajs.github.io/fork) directly modify the error, which should be the better option, and we welcome any suggestions or contributions to improve the documentation.
+
+### Translate Documentation
+
+If you are proficient in different languages, you are welcome to translate the alova documentation, which will help expand the use and audience of alova.
+
+## Become a core team member
+
+Check [HERE](./become-core-member) for details
diff --git a/docs/contributing/02-become-core-member.md b/docs/contributing/02-become-core-member.md
index 501677561..0ab350025 100644
--- a/docs/contributing/02-become-core-member.md
+++ b/docs/contributing/02-become-core-member.md
@@ -1,32 +1,31 @@
----
-title: Become a core team member
-sidebar_position: 20
----
-
-🤝🤝🤝 If you also agree with alovajs’ philosophy, then let us create the next generation request library together!
-
-## Responsibilities
-
-1. Responsible for the development and maintenance of vscode plug-in projects, request scenario modules and adapter projects, as well as the Hongmeng version of alovajs.
-2. Write and optimize relevant development documents.
-3. In-depth participation in the entire life cycle of project design and implementation, promotion and maintenance.
-4. Participate in project development decision-making and commercialization exploration.
-
-## Revenue
-
-1. With the title of core member, you have the opportunity to enhance your reputation in the front-end field, thereby broadening your career development path.
-2. More opportunities for public sharing and opportunities to contact more front-end personnel.
-3. Broaden project thinking and technical experience through in-depth participation in project design, implementation and operation.
-4. Decision-making power on projects.
-5. Profit sharing.
-
-## Require
-
-1. Agree with alova’s design philosophy and development direction, and be willing to work hard to move forward together.
-2. Passion for technology, sense of responsibility, and teamwork.
-3. Technical capabilities only need to cover existing needs.
-4. Have some spare time.
-
-## How to join
-
-If you are interested, please send an email to [hujou555@gmail.com](mailto:hujou555@gmail.com), and be sure to add `alova core member application` at the beginning of email.
+---
+title: Become a core team member
+---
+
+🤝🤝🤝 If you also agree with alovajs’ philosophy, then let us create the next generation request library together!
+
+## Responsibilities
+
+1. Responsible for the development and maintenance of vscode plug-in projects, request scenario modules and adapter projects, as well as the Hongmeng version of alovajs.
+2. Write and optimize relevant development documents.
+3. In-depth participation in the entire life cycle of project design and implementation, promotion and maintenance.
+4. Participate in project development decision-making and commercialization exploration.
+
+## Revenue
+
+1. With the title of core member, you have the opportunity to enhance your reputation in the front-end field, thereby broadening your career development path.
+2. More opportunities for public sharing and opportunities to contact more front-end personnel.
+3. Broaden project thinking and technical experience through in-depth participation in project design, implementation and operation.
+4. Decision-making power on projects.
+5. Profit sharing.
+
+## Require
+
+1. Agree with alova’s design philosophy and development direction, and be willing to work hard to move forward together.
+2. Passion for technology, sense of responsibility, and teamwork.
+3. Technical capabilities only need to cover existing needs.
+4. Have some spare time.
+
+## How to join
+
+If you are interested, please send an email to [hujou555@gmail.com](mailto:hujou555@gmail.com), and be sure to add `alova core member application` at the beginning of email.
diff --git a/docs/contributing/03-developing-guidelines.md b/docs/contributing/03-developing-guidelines.md
index 357669268..b13c40096 100644
--- a/docs/contributing/03-developing-guidelines.md
+++ b/docs/contributing/03-developing-guidelines.md
@@ -1,138 +1,137 @@
----
-title: Developing Guidelines
-sidebar_position: 30
----
-
-:::info version required
-
-Node.js 16+, npm 8+
-
-:::
-
-## 1. Fork repository
-
-[Open alova fork page](https://github.com/alovajs/alova/fork), click "Create fork" and clone the forked repository to local.
-
-## 2. Clone to local
-
-Use the `git clone` command line, or the `Github Desktop` application to clone forked project.
-
-## 3. New pull request
-
-You can [create pull request through a forked repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) after writing code. You can also commit code in arbitrary batches, without commiting a complete code.
-
-## 4. Code something in your computer
-
-### Install dependencies
-
-Install dependencies using `npm install`.
-
-### Install the recommended plugin(vscode)
-
-If you are using vscode, it is recommended that you install the following plugins:
-
-- eslint: check code quality.
-- prettier: formatting code.
-- jest: Automatically execute unit test cases, and execute individual collection or unit test cases.
-- EditorConfig: Make sure the file format is consistent.
-
-### Project Structure
-
-```
-|-.github
-| |-ISSUE_TEMPLATE -> github issues template
-| |-workflows -> github action
-|-.husky -> husky configuration
-|-.vscode -> vscode configuration
-|-config -> rollup package files
-|-src -> source code
-|-test -> unit test suits
-| |-browser -> browser environment unit test suits
-| |-server -> SSR unit test suits
-| |-components -> Unit test components
-| |-mockServer.ts -> mock apis (msw)
-|-typings -> ts declaration
-|- Other configuration files
-
-```
-
-### Coding specifications
-
-#### Code format
-
-If you install the `prettier` plugin, it will automatically format codes every time you save files, so you don't have to worry about the format.
-
-#### Minimize code
-
-Lightweight is one of the alova's features, so it is necessary to minimize the amount of coding when coding. Here are a few coding specifications that need to be followed:
-
-1. Avoid the same code block, which can reduce the amount of code in the library, but two lines of code may not be worth encapsulating;
-2. Use a variable declarator to aggregate variable declarations, for example:
-
-```javascript
-// ❌
-const a = 1;
-const b = 2;
-
-// ✅
-const a = 1,
- b = 2;
-```
-
-3. Use constants to save js built-in values and prototype methods to reduce the amount of code in the compilation phase of `uglify`. built-in values and prototype methods that often used are defined in `src/utils/variables.ts`.
-
-```javascript
-// ❌
-if (a === false) {
- //...
-}
-arr.forEach(item => {
- //...
-});
-
-// ✅
-import { falseValue, forEach } from '@/utils/variables';
-if (a === falseValue) {
- //...
-}
-forEach(arr, item => {
- //...
-});
-```
-
-## 5. Unit Testing Guidelines
-
-After finish code, it is necessary to add corresponding unit tests.
-
-The alova project uses **jest** as the unit test framework, and msw as the mock server. It is recommended to use the TDD mode. After modifying code every time, please pass the corresponding unit test.
-
-:::warning IMPORTANT
-
-When you're ready to commit your code, make sure all your unit tests are passed. When you're working on a pull request, you can have multiple small commits, and GitHub can automatically squash them before merging them.
-
-:::
-
-1. To add browser-related unit test cases, please add them to the corresponding test collection in `test/browser`, if there is no suitable test suits, you can create one by yourself;
-2. Add SSR-related unit test cases, please add them to the corresponding test collection in `test/server`, if there is no suitable test suits, you can create one by yourself;
-
-### Run and debug a single unit test or suits
-
-It is recommended to use the **jest** plugin (one of the plugins recommended above) to test a single use case or a suit. You can right-click the specified unit test, select `Run Test` to run it, and select `Debug Test` to debug it with breakpoint.
-
-![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea)
-
-### Run all unit tests
-
-1. Use the **jest** plugin to run:
-
-![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181)
-
-2. Run the browser unit tests with command line `npm run test:browser`, run the SSR unit tests with `npm run test:node`, and run both at the same time with `npm run test`.
-
-## 6. Commit codes
-
-alova uses [semantic-release](https://semantic-release.gitbook.io) as an automatic release tool, which can automatically release new version packages after merging code into `main` , and generate `CHANGELOG`, but you need to ensure that the committed message format follows [commit information convention](https://www.conventionalcommits.org/en/v1.0.0/), it is recommended that use `npm run commit` to automatically generate a git message that conforms to the specification.
-
-## 7. Writing docs
-
-If you are adding a new feature, you can try to add the relevant documentation of the new feature. For details, please read [Correcting or add docs](/contributing/overview#correct-or-add-docs), otherwise please explain it in the pull request.
+---
+title: Developing Guidelines
+---
+
+:::info version required
+
+Node.js 16+, npm 8+
+
+:::
+
+## 1. Fork repository
+
+[Open alova fork page](https://github.com/alovajs/alova/fork), click "Create fork" and clone the forked repository to local.
+
+## 2. Clone to local
+
+Use the `git clone` command line, or the `Github Desktop` application to clone forked project.
+
+## 3. New pull request
+
+You can [create pull request through a forked repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) after writing code. You can also commit code in arbitrary batches, without commiting a complete code.
+
+## 4. Code something in your computer
+
+### Install dependencies
+
+Install dependencies using `npm install`.
+
+### Install the recommended plugin(vscode)
+
+If you are using vscode, it is recommended that you install the following plugins:
+
+- eslint: check code quality.
+- prettier: formatting code.
+- jest: Automatically execute unit test cases, and execute individual collection or unit test cases.
+- EditorConfig: Make sure the file format is consistent.
+
+### Project Structure
+
+```
+|-.github
+| |-ISSUE_TEMPLATE -> github issues template
+| |-workflows -> github action
+|-.husky -> husky configuration
+|-.vscode -> vscode configuration
+|-config -> rollup package files
+|-src -> source code
+|-test -> unit test suits
+| |-browser -> browser environment unit test suits
+| |-server -> SSR unit test suits
+| |-components -> Unit test components
+| |-mockServer.ts -> mock apis (msw)
+|-typings -> ts declaration
+|- Other configuration files
+
+```
+
+### Coding specifications
+
+#### Code format
+
+If you install the `prettier` plugin, it will automatically format codes every time you save files, so you don't have to worry about the format.
+
+#### Minimize code
+
+Lightweight is one of the alova's features, so it is necessary to minimize the amount of coding when coding. Here are a few coding specifications that need to be followed:
+
+1. Avoid the same code block, which can reduce the amount of code in the library, but two lines of code may not be worth encapsulating;
+2. Use a variable declarator to aggregate variable declarations, for example:
+
+```javascript
+// ❌
+const a = 1;
+const b = 2;
+
+// ✅
+const a = 1,
+ b = 2;
+```
+
+3. Use constants to save js built-in values and prototype methods to reduce the amount of code in the compilation phase of `uglify`. built-in values and prototype methods that often used are defined in `src/utils/variables.ts`.
+
+```javascript
+// ❌
+if (a === false) {
+ //...
+}
+arr.forEach(item => {
+ //...
+});
+
+// ✅
+import { falseValue, forEach } from '@/utils/variables';
+if (a === falseValue) {
+ //...
+}
+forEach(arr, item => {
+ //...
+});
+```
+
+## 5. Unit Testing Guidelines
+
+After finish code, it is necessary to add corresponding unit tests.
+
+The alova project uses **jest** as the unit test framework, and msw as the mock server. It is recommended to use the TDD mode. After modifying code every time, please pass the corresponding unit test.
+
+:::warning IMPORTANT
+
+When you're ready to commit your code, make sure all your unit tests are passed. When you're working on a pull request, you can have multiple small commits, and GitHub can automatically squash them before merging them.
+
+:::
+
+1. To add browser-related unit test cases, please add them to the corresponding test collection in `test/browser`, if there is no suitable test suits, you can create one by yourself;
+2. Add SSR-related unit test cases, please add them to the corresponding test collection in `test/server`, if there is no suitable test suits, you can create one by yourself;
+
+### Run and debug a single unit test or suits
+
+It is recommended to use the **jest** plugin (one of the plugins recommended above) to test a single use case or a suit. You can right-click the specified unit test, select `Run Test` to run it, and select `Debug Test` to debug it with breakpoint.
+
+![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea)
+
+### Run all unit tests
+
+1. Use the **jest** plugin to run:
+
+![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181)
+
+2. Run the browser unit tests with command line `npm run test:browser`, run the SSR unit tests with `npm run test:node`, and run both at the same time with `npm run test`.
+
+## 6. Commit codes
+
+alova uses [semantic-release](https://semantic-release.gitbook.io) as an automatic release tool, which can automatically release new version packages after merging code into `main` , and generate `CHANGELOG`, but you need to ensure that the committed message format follows [commit information convention](https://www.conventionalcommits.org/en/v1.0.0/), it is recommended that use `npm run commit` to automatically generate a git message that conforms to the specification.
+
+## 7. Writing docs
+
+If you are adding a new feature, you can try to add the relevant documentation of the new feature. For details, please read [Correcting or add docs](/next/contributing/overview#correct-or-add-docs), otherwise please explain it in the pull request.
diff --git a/docs/contributing/04-code-of-conduct.md b/docs/contributing/04-code-of-conduct.md
index 580d03abe..833a9ab0c 100644
--- a/docs/contributing/04-code-of-conduct.md
+++ b/docs/contributing/04-code-of-conduct.md
@@ -1,137 +1,136 @@
----
-title: Code of Conduct
-sidebar_position: 40
----
-
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-We as members, contributors, and leaders pledge to make participation in our
-community a harassment-free experience for everyone, regardless of age, body
-size, visible or invisible disability, ethnicity, sex characteristics, gender
-identity and expression, level of experience, education, socio-economic status,
-nationality, personal appearance, race, caste, color, religion, or sexual
-identity and orientation.
-
-We pledge to act and interact in ways that contribute to an open, welcoming,
-diverse, inclusive, and healthy community.
-
-## Our Standards
-
-Examples of behavior that contributes to a positive environment for our
-community include:
-
-- Demonstrating empathy and kindness toward other people
-- Being respectful of differing opinions, viewpoints, and experiences
-- Giving and gracefully accepting constructive feedback
-- Accepting responsibility and apologizing to those affected by our mistakes,
- and learning from the experience
-- Focusing on what is best not just for us as individuals, but for the overall
- community
-
-Examples of unacceptable behavior include:
-
-- The use of sexualized language or imagery, and sexual attention or advances of
- any kind
-- Trolling, insulting or derogatory comments, and personal or political attacks
-- Public or private harassment
-- Publishing others' private information, such as a physical or email address,
- without their explicit permission
-- Other conduct which could reasonably be considered inappropriate in a
- professional setting
-
-## Enforcement Responsibilities
-
-Community leaders are responsible for clarifying and enforcing our standards of
-acceptable behavior and will take appropriate and fair corrective action in
-response to any behavior that they deem inappropriate, threatening, offensive,
-or harmful.
-
-Community leaders have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are
-not aligned to this Code of Conduct, and will communicate reasons for moderation
-decisions when appropriate.
-
-## Scope
-
-This Code of Conduct applies within all community spaces, and also applies when
-an individual is officially representing the community in public spaces.
-Examples of representing our community include using an official e-mail address,
-posting via an official social media account, or acting as an appointed
-representative at an online or offline event.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported to the community leaders responsible for enforcement at
-[INSERT CONTACT METHOD].
-All complaints will be reviewed and investigated promptly and fairly.
-
-All community leaders are obligated to respect the privacy and security of the
-reporter of any incident.
-
-## Enforcement Guidelines
-
-Community leaders will follow these Community Impact Guidelines in determining
-the consequences for any action they deem in violation of this Code of Conduct:
-
-### 1. Correction
-
-**Community Impact**: Use of inappropriate language or other behavior deemed
-unprofessional or unwelcome in the community.
-
-**Consequence**: A private, written warning from community leaders, providing
-clarity around the nature of the violation and an explanation of why the
-behavior was inappropriate. A public apology may be requested.
-
-### 2. Warning
-
-**Community Impact**: A violation through a single incident or series of
-actions.
-
-**Consequence**: A warning with consequences for continued behavior. No
-interaction with the people involved, including unsolicited interaction with
-those enforcing the Code of Conduct, for a specified period of time. This
-includes avoiding interactions in community spaces as well as external channels
-like social media. Violating these terms may lead to a temporary or permanent
-ban.
-
-### 3. Temporary Ban
-
-**Community Impact**: A serious violation of community standards, including
-sustained inappropriate behavior.
-
-**Consequence**: A temporary ban from any sort of interaction or public
-communication with the community for a specified period of time. No public or
-private interaction with the people involved, including unsolicited interaction
-with those enforcing the Code of Conduct, is allowed during this period.
-Violating these terms may lead to a permanent ban.
-
-### 4. Permanent Ban
-
-**Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior, harassment of an
-individual, or aggression toward or disparagement of classes of individuals.
-
-**Consequence**: A permanent ban from any sort of public interaction within the
-community.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage],
-version 2.1, available at
-[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
-
-Community Impact Guidelines were inspired by
-[Mozilla's code of conduct enforcement ladder][mozilla coc].
-
-For answers to common questions about this code of conduct, see the FAQ at
-[https://www.contributor-covenant.org/faq][faq]. Translations are available at
-[https://www.contributor-covenant.org/translations][translations].
-
-[homepage]: https://www.contributor-covenant.org
-[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
-[mozilla coc]: https://github.com/mozilla/diversity
-[faq]: https://www.contributor-covenant.org/faq
-[translations]: https://www.contributor-covenant.org/translations
+---
+title: Code of Conduct
+---
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the overall
+ community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email address,
+ without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+[INSERT CONTACT METHOD].
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][mozilla coc].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][faq]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[mozilla coc]: https://github.com/mozilla/diversity
+[faq]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/docs/contributing/_category_.json b/docs/contributing/_category_.json
index d3bf2a858..66f0e1824 100644
--- a/docs/contributing/_category_.json
+++ b/docs/contributing/_category_.json
@@ -1,6 +1,6 @@
-{
- "label": "Contributing",
- "link": {
- "type": "generated-index"
- }
-}
+{
+ "label": "Contributing",
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/error.md b/docs/error.md
new file mode 100644
index 000000000..da4f4fb7f
--- /dev/null
+++ b/docs/error.md
@@ -0,0 +1,5 @@
+---
+title: Error Reference
+---
+
+Coming soon...
diff --git a/docs/release-notes-v3.md b/docs/release-notes-v3.md
new file mode 100644
index 000000000..bc02d60b5
--- /dev/null
+++ b/docs/release-notes-v3.md
@@ -0,0 +1,612 @@
+---
+title: alova v3.0 Release Notes
+---
+
+:::warning beta reminder
+
+alova@3.0 is currently in the beta stage, and some functions may be changed. If you find a bug, please let us know in [Github issues](https://github.com/alovajs/alova/issues/new/choose), and we will solve it as soon as possible.
+
+:::
+
+## Overall upgrade goal
+
+alova@3.0 aims to further achieve the goal of "Run in any JS environment" and make it easier to use. We have fully redesigned and refactored the code in 3.0 to make it more user-friendly on the server and in more JS environments.
+
+## Redesign alova
+
+### Redesign the structure
+
+1. Change the fetch adapter export path to `alova/fetch/`
+
+```javascript
+const adapterFetch = require('alova/fetch');
+const alova = createAlova({
+ requestAdapter: adapterFetch()
+});
+```
+
+2. JS package structure adjustment
+
+- Merge the original `@alova/scene-vue`, `@alova/scene-react`, `@alova/scene-svelte` into a package `alova/client` using cross-UI framework technology.
+
+- Move `useRequest/useWatcher/useFetcher` to `alova/client`.
+
+- Add `alova/server` to export the server-side scene strategy module.
+
+Now you can directly use `alova/client` and `alova/server` as long as you install `alova`.
+
+```javascript
+import vueHook from 'alova/vue';
+import reactHook from 'alova/react';
+import svelteHook from 'alova/svelte';
+import vueDemiHook from 'alova/vue-demi';
+import { useRequest, useWatcher, usePagination } from 'alova/client';
+import adapterFetch from 'alova/fetch';
+
+const alova = createAlova({
+ statesHook: vueHook,
+ requestAdapter: adapterFetch()
+});
+const { data, loading, error } = useRequest(alova.Get('/api/user'));
+```
+
+server hooks usage example
+
+```javascript
+const { useRateLimit, sendCaptcha } = require('alova/server');
+import adapterFetch from 'alova/fetch';
+const alova = createAlova({
+ requestAdapter: adapterFetch()
+});
+const requestCaptcha = mobile => alova.Get('/api/captcha', { params: { mobile } });
+try {
+ await sendCaptcha(requestCaptcha, { key: mobile, countdown: 60 });
+} catch (error) {
+ throw new Error('Send failed');
+}
+```
+
+### Redesign cache mode
+
+In order to make alova shine on the server side and simplify alova's API, we considered the following cache application scenarios
+Application scenarios
+
+1. High access frequency and low latency requirements, such as hot news and product details, can further reduce network overhead and maintain faster response when the network is unstable.
+2. Reduce the pressure on downstream servers. For example, for services with peak access periods, the upper-level cache can effectively reduce the pressure on the backend database and microservices.
+3. Integrate data merging and processing of multiple downstream servers. Multiple serial requests may lead to longer response time and may consume performance due to complex data conversion. The converted data can be cached.
+4. API rate limit and billing. Weather forecast service API updates weather information every hour, geographic location data API, etc.
+
+As well as most asynchronous cache processing mechanisms, the following redesigns have been made:
+
+#### 1. Remove the placeholder mode
+
+Placeholder is only valid on the client and with useHook. In 3.0, initialData is added to support functions to achieve the same effect, as follows:
+
+```javascript
+const { onSuccess } = useRequest(Getter, {
+ initialData() {
+ // Set the last response data
+ const storedData = localStorage.getItem('placeholder-data');
+ return JSON.parse(storedData || '{}');
+
+ // Also use alova's storage adapter
+ // return alovaInst.l2cache.get('placeholder-data');
+ }
+});
+onSuccess(({ data, method }) => {
+ // Save response data
+ localStorage.setItem('placeholder-data', JSON.stringify(data));
+
+ // Also use alova's storage adapter
+ alovaInst.l2cache.set('placeholder-data', data);
+});
+```
+
+At this point, there are only two cache modes, the definition and usage remain unchanged, but the memory mode supports custom adapters and asynchronous operations, as follows:
+
+- **memory mode**: supports custom memory adapters and asynchronous operations, for example, it can be customized as LRUCache in the server scenario, and can also manage processes across processes, or manage caches anywhere else you want.
+
+- **restore mode**: response data will be cached in both memory and storage. When requesting, the memory cache will be searched first. If not found, it will be searched in storage. After a hit, the cache will be restored to the memory cache. If both miss, a request will be initiated.
+
+#### 2. Memory cache and store cache support asynchronous operations
+
+We can use memory as the first-level cache and storage cache as the second-level cache.
+
+> Note: In terms of the use of a single cache, there is no difference between memory mode and storage mode. Both support custom adapters and asynchronous operations. You can also customize the cache to be placed anywhere, but the implementation mechanism is that the first-level cache takes precedence over the second-level cache. In restore mode, you can implement the mechanism of the second-level cache restoring the first-level cache.
+
+Scenario recommendation: Memory mode uses local memory (including cross-process sharing in the cluster), and storage mode is placed in a persistent cache such as a file or Redis.
+
+```js
+const { LRUCache } = require('lru-cache');
+const { createClient } = require('redis');
+
+const client = createClient(/*...*/);
+const alovaInst = createAlova({
+ // ...
+ // l1 is the highest priority cache, that is, the original memory cache
+ l1Cache: new LRUCache(),
+ // l2 is a lower memory cache, that is, the original persistent cache
+ l2Cache: {
+ set: async (key, [data, expireTs]) => {
+ await client.set(key, data, {
+ EX: Number((expireTs - Date.now()) / 1000)
+ });
+ },
+ get: async key => client.get(key),
+ remove: async key => client.set(key, '', 0)
+ }
+});
+```
+
+An example of handling cache avalanche, solved by adding random expiration time
+
+```javascript
+alova.Get('/xxx', {
+ cacheFor: {
+ expire: 100000 * Math.floor(Math.random() * 1000),
+ mode: 'restore'
+ }
+});
+```
+
+#### 3. Cache operation function optimization
+
+1. In order to adapt to asynchronous storage adapters, cache operation functions `setCache`, `queryCache`, `invalidateCache` are changed to asynchronous functions.
+2. Add separate control for level 1 and level 2 cache.
+3. `setCache/queryCache/invalidateCache` no longer supports method matcher parameters. For details, see the method snapshot matcher modification section below.
+
+```js
+await setCache(methodInstance, data, {
+ /**
+ * Cache policy.
+ * - l1: only set l1 cache.
+ * - l2: only set l2 cache.
+ * - all: set l1 cache and set l2 cache (method cache mode needs to be "restore").
+ * @default all
+ */
+ policy: 'all'
+});
+
+/**
+ * Cache policy.
+ * - l1: only query l1 cache.
+ * - l2: only query l2 cache.
+ * - all: query l1 cache first, and query l2 cache if l1 cache is not found (method cache mode needs to be "restore").
+ * @default all
+ */
+await queryCache(methodInstance, {
+ policy: 'all'
+});
+await invalidateCache([methodInstance1, methodInstance2 /*...*/]);
+```
+
+#### 4. Set different cache expiration times
+
+In high access frequency and low latency requirements, we often need to cache high-frequency access data in the local server for a short time, and set a cache with a longer expiration time in the remote cache server. At this time, we can do this:
+
+```js
+// Global settings
+const alovaInst = createAlova({
+ // ...
+ cacheFor: {
+ GET: {
+ mode: 'restore',
+ expire: ({ method, mode }) => {
+ if (method.meta.setDiffExpire) {
+ // Set a 5-minute cache in l1 cache and a 1-day cache in l2 cache
+ return mode === 'memory' ? 5 * 60 : 24 * 60 * 60;
+ }
+ }
+ }
+ }
+});
+
+// Set for a single request
+alovaInst.Get('/user/profile', {
+ // ...
+ cacheFor: {
+ mode: 'restore',
+ expire: ({ method, mode }) => {
+ // Set 5 minutes cache in l1 cache, 1 day cache in l2 cache
+ return mode === 'memory' ? 5 * 60 : 24 * 60 * 60;
+ }
+ }
+});
+```
+
+### method snapshot matcher modification
+
+The original method matcher can be used in `setCache`, `queryCache`, `invalidateCache`ache`, `useFetcher.fetch`, `updateState`, but the number of method instances they require is different. In addition, there is an ambiguity problem. Only method instances that have been requested are saved in the matcher container (memory). However, when the page is refreshed, when the persistent cache is obtained or invalidated, for example, `queryCache('method-name')`or`invalidateCache('method-name')` may be invalid because the method instance cannot be found in the snapshot, causing ambiguity.
+
+Therefore, in alova@3, the method matcher is externalized, and the above five functions are changed to only support the passing of method instances. Users can clearly know whether the method instance snapshot has been found, which is more unified in usage and does not cause ambiguity. The code design is as follows:
+
+```js
+// alova@2 writing
+invalidateCache('method-name');
+const data = queryCache('method-name2');
+
+// alova@3 writing
+const methodSnapshots = alovaInst.snapshots.match('method-name'); // Match multiple
+invalidateCache(methodSnapshots);
+const oneSnapshot = alovaInst.snapshots.match('method-name2', true); // Match only one
+const data = await queryCache(oneSnapshot);
+```
+
+### Rewrite snapshot matching and automatic invalidation cache algorithm
+
+In 2.x, snapshot matching and automatic invalidation cache algorithms both search for the target method by traversing method snapshots. On the server side or long-running clients, the matching efficiency may be reduced due to too many snapshots, which will cause performance loss. In 3.x, the search steps will be reduced to improve the search efficiency.
+Export new function:
+
+```js
+declare function hitCacheBySource(sourceMethod: Method): Promise;
+await hitCacheBySource(alova.Get('/api/profile'));
+```
+
+## Deprecated items
+
+### Deprecated useWatcher's sendable
+
+Deprecated useWatcher's sendable, judged by middleware.
+
+```typescript
+// alova@2.x
+let sendable = false;
+useWatcher(() => method, [xxx], { sendable: () => sendable });
+
+// alova@3.x
+let sendable = false;
+useWatcher(() => method, [xxx], {
+ async middleware(_, next) {
+ if (sendable) {
+ return next();
+ }
+ }
+});
+```
+
+### Deprecate `enableDownload/enableUpload` of method
+
+These two parameters are no longer needed in method. They will be changed to automatically determine whether to enable or not. Getter will be used to determine whether uploading and downloading are used externally.
+
+### Deprecate `placeholder` mode of method
+
+We have mentioned in "redesign cache mode", `placeholder` mode has been replaced by other ways.
+
+### Remove deprecated responsed
+
+It is recommended to use the `responded` field uniformly in createAlova.
+
+```js
+createAlova({
+ responded() {
+ // ...
+ }
+});
+```
+
+### Deprecated error log control parameter errorLogger
+
+In 2.x, an error message will be printed in the console whenever there is a request error. When the error is not caught at the request, the console will display two identical error messages, which is not very friendly.
+
+In 3.x, if onError is bound or error is used, no error will be thrown. Otherwise, an error will be thrown to avoid displaying two error messages.
+
+### Deprecated `onMatch` hook of `updateState`
+
+Because updateState no longer supports method matchers, the `onMatch` hook is no longer useful.
+
+### Deprecated `matchSnapshotMethod`
+
+In alova@3.0, `alova.snapshots.match` is used to obtain the instance snapshot under the corresponding alova.
+
+```js
+alova.snapshots.match('method-name');
+```
+
+### Deprecated `getMethodKey`
+
+alova@3.x no longer exports `getMethodKey`, you can import the function that calculates the method key from the following methods.
+
+```js
+import { key } from '@alova/shared/function';
+
+const methodKey = key(methodInstance);
+
+// or
+const currentKey = methodInstance.generateKey();
+```
+
+### Deprecated middleware items
+
+1. Deprecated `context.update` function.
+2. Deprecate event decorations such as `decorateSuccess/decorateError/decorateComplete`. Now you can decorate events like this:
+
+```js
+import { decorateEvent } from '@alova/shared/createEventManager';
+
+const exposure = useRequest(/* ... */);
+exposure.onSuccess = decorateEvent(exposure.onSuccess, (handler, event) => {
+ event.extraAttribute = {
+ /* ... */
+ }; // Mount extra attributes for event
+ const res = handler(event, 1, 2, 3); // Pass extra parameters to event callback and get return value
+ // ...
+});
+
+exposure.onSuccess((event, extra1, extra2, extra3) => {
+ event.extraAttribute; // Extra attributes
+ extra1; // 1
+ extra2; // 2
+ extra3; // 3
+ return 100;
+});
+```
+
+### @alova/vue-options deprecates `mapWatcher`
+
+Listening to the return status of useHook no longer requires the use of the auxiliary function `mapWatcher`, so it is deprecated.
+
+```js
+// @alova/vue-options@1.x
+export default {
+ mixins: mapAlovaHook(function () {
+ return {
+ testRequest: useRequest(this.method)
+ };
+ }),
+
+ watch: mapWatcher({
+ 'testRequest.data'() {
+ // ...
+ }
+ })
+}
+
+// @alova/vue-options@2.0
+export default {
+ // ...
+ watch: {
+ 'testRequest.data'() {
+ // ...
+ }
+ }
+}
+```
+
+## @alova/scene-\* optimization
+
+We have optimized some strategies of `@alova/scene-*` and moved them to `alova/client`.
+
+### usePagination
+
+#### 1. Set abortLast to true by default
+
+When continuously turning pages or querying data, only the content of the last operation will be displayed, which is more in line with the experience.
+
+```js
+// 2.x requires explicit settings
+usePagination({
+ // ...
+ abortLast: true
+});
+
+// 3.x does not require
+usePagination({
+ // ...
+});
+```
+
+#### Optimize the use under react
+
+Under react, the page and pageSize export items are no longer a reactState, and now update is used uniformly to change the state.
+
+```js
+// 2.x
+const {
+ page: [page, setPage],
+ pageSize: [pageSize, setPageSize]
+ /*...*/
+} = usePagination(/*...*/);
+const handleAddPage = () => {
+ setPage(page + 1);
+};
+const handleSetPageSize = newPageSize => {
+ setPageSize(newPageSize);
+};
+
+// 3.x
+const { page, pageSize, update /*...*/ } = usePagination(/*...*/);
+const handleAddPage = () => {
+ update({ page: page + 1 });
+};
+const handleSetPageSize = newPageSize => {
+ update({ pageSize: newPageSize });
+};
+```
+
+### useForm
+
+#### 1. No longer supports directly passing in id to return cached hookReturns
+
+Due to the original scene The package is directly divided into three packages: `@alova/scene-react`, `@alova/scene-vue`, and `@alova/scene-svelte`. The export status of each package is fixed to the status type of the corresponding UI framework. After the revision, it is unified into a client library. When submitting a multi-step form, it is also necessary to provide a method instance or methodHandler to automatically infer which UI framework's status type should be exported, but the function remains unchanged.
+
+```js
+// In 2.x, states are the state types of the corresponding UI framework
+const states = useForm('form-id');
+
+// In 3.0, a method instance or methodHandler must also be provided
+const { states, update } = useForm(formData => methodHandler(formData), {
+ // ...
+ id: 'form-id'
+});
+```
+
+### useSQRequest
+
+#### 1. `updateStateEffect` no longer supports the use of method matchers
+
+Since updateState no longer supports the use of method matchers, `updateStateEffect` is modified synchronously, and the `onMatch` callback function is no longer supported.
+
+#### 2. Modification of `SilentMethod` member method usage
+
+Since l2Cache can be set as an asynchronous function, `SilentMethod.replace/remove/save` and `filterSilentMethods/getSilentMethod` are changed from synchronous functions to asynchronous functions that return promises.
+
+```js
+import { filterSilentMethods, getSilentMethod } from 'alova/client';
+
+const silentMethods = await filterSilentMethods('method-name', 'queue-name');
+const silentMethod = await getSilentMethod('method-name', 'queue-name');
+
+await silentMethod.replace(newSilentMethod);
+await silentMethod.remove();
+await silentMethod.save();
+```
+
+### accessAction
+
+Added silent parameter, controlled by silent parameter, no error is thrown when delegation is not matched
+
+```js
+// Error is thrown when method-name is not matched
+accessAction('method-name', ({ send }) => {
+ send();
+});
+
+// No error is thrown at this time
+accessAction(
+ 'method-name',
+ ({ send }) => {
+ send();
+ },
+ true
+);
+```
+
+### All useHook's force function is changed to AlovaEvent object
+
+```js
+// @2.x
+useRequest(Getter, {
+ force(arg1, arg2) {
+ // ...
+ }
+});
+
+// 3.x
+useRequest(Getter, {
+ force(event) {
+ // Get args
+ const arg1 = event.sendArgs[0];
+ const arg2 = event.sendArgs[1];
+
+ // Get method
+ const method = event.method;
+ }
+});
+```
+
+## Field name modification
+
+### `method.__key__` is simplified to `method.key`
+
+```js
+// 2.x
+method.__key__ = 'custom-key';
+
+// 3.x
+method.key = 'custom-key';
+```
+
+### `method.transformData` is simplified to `method.transform`
+
+```js
+// 2.x
+alova.Get('/api/profile', {
+ transformData(data) {
+ return data.detail;
+ }
+});
+
+// 3.x
+alova.Get('/api/profile', {
+ transform(data) {
+ return data.detail;
+ }
+});
+```
+
+### `method.localCache` changed to `method.cacheFor`
+
+```js
+// 2.x
+alova.Get('/api/profile', {
+ localCache: 1000 * 60 * 60
+});
+
+// 3.x
+alova.Get('/api/profile', {
+ cacheFor: 1000 * 60 * 60
+});
+```
+
+### @alova/adapter-uniapp export name change
+
+Since alova's `storageAdapter` was renamed to `l2Cache`, the export item `uniappStorageAdapter` of `@alova/adapter-uniapp` was changed to `uniappL2CacheAdapter`.
+
+## Other optimizations
+
+### Support dependency collection
+
+This is a performance optimization. All useHooks in alova@3.x will not update unaccessed states to reduce redundant view rendering caused by internal state updates.
+
+A simple example is as follows. When `loading` is not used, `useRequest` will not update the `loading` state to reduce additional view rendering.
+
+```js
+const App = () => {
+ const { data } = useRequest(method1);
+ return data ?
{data.name}
: null;
+};
+
+export default App;
+```
+
+### middleware optimization
+
+In alova@2.x, accessing states data in middleware is not flexible enough because you get a framework-related state collection, which is not conducive to writing general middleware. Therefore, the following changes are made in 3.x:
+
+1. Use the framework-independent proxyState to access and modify the state so that the middleware can be used in any UI framework.
+
+```js
+middleware({ proxyStates, args }, ({ update }) => {
+ // Access state
+ const loadingValue = proxyStates.loading.v;
+
+ // Modify state
+ proxyStates.loading.v = true;
+});
+```
+
+2. sendArgs and fetchArgs are changed to args.
+
+### All event binding functions return their own objects
+
+In order to optimize the user experience and reduce renaming, all event binding functions return the export object of useHook to obtain the effect of chain call binding events.
+
+```js
+// 2.x
+const { onSuccess, loading, data } = useRequest(method);
+onSuccess(event => {
+ // ...
+});
+const { onSuccess: onSuccess2, error } = useRequest(method2);
+onSuccess2(event => {
+ // ...
+});
+
+// 3.x
+const { loading, data } = useRequest(method).onSuccess(event => {
+ // ...
+});
+const { error } = useRequest(method2).onSuccess(event => {
+ // ...
+});
+```
diff --git a/docs/resource/01-request-adapter/01-alova-mock.md b/docs/resource/01-request-adapter/01-alova-mock.md
new file mode 100644
index 000000000..4f6d06acf
--- /dev/null
+++ b/docs/resource/01-request-adapter/01-alova-mock.md
@@ -0,0 +1,330 @@
+---
+title: Mock data
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the Scope of usage of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off.
+
+## Features
+
+- Works seamlessly with alova
+- Arbitrary grouping of simulation requests to control global, group, and individual simulation interface enable and disable
+- Works with mockjs
+- Do not pollute the production environment
+
+## Install
+
+
+
+
+```bash
+npm install @alova/mock@beta --save
+```
+
+
+
+
+```bash
+yarn add @alova/mock@beta
+```
+
+
+
+
+The following is the usage flow.
+
+## Usage
+
+### Define the mock interface
+
+Use `defineMock` to define a set of mock interfaces. You can directly specify the return response data in each mock interface, or specify the response data to be dynamically calculated for the callback function.
+
+```javascript title=mockGrou1.js
+import { defineMock } from '@alova/mock';
+
+export default defineMock(
+ {
+ // capture get request
+ '/todo': [1, 2, 3, 4],
+
+ // rest style request
+ '/todo/{id}': ({ params }) => {
+ const id = params.id;
+ // ...
+ return {
+ title: '...',
+ time: '10:00'
+ };
+ },
+
+ // capture post request
+ '[POST]/todo': ({ query, data }) => {
+ // ...
+ return { success: true };
+ },
+
+ // return more detailed information
+ '[POST]/todo': ({ query, data }) => {
+ //...
+ return {
+ status: 403,
+ statusText: 'unknown error',
+ responseHeaders: {
+ //...
+ },
+ body: {
+ success: true
+ }
+ };
+ },
+
+ // simulate network error
+ '[POST]/todo': ({ query, data }) => {
+ throw new Error('network error');
+ },
+
+ // Add `-` before the key to disable this mock interface
+ '-[DELETE]/todo/{id}': ({ params }) => {
+ // ...
+ return { success: true };
+ }
+ },
+ true
+); // The second parameter indicates whether to enable this group of mock interfaces, the default is true, and can be specified as false to close
+```
+
+### Create mock request adapter
+
+Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete.
+
+```javascript
+import adapterFetch from 'alova/fetch';
+import { createAlovaMockAdapter } from '@alova/mock';
+import mockGroup1 from './mockGroup1';
+
+// highlight-start
+const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], {
+ // Global control whether the mock interface is enabled, the default is true
+ enable: true,
+
+ // Non-mock request adapter, used to send requests when the mock interface is not matched
+ httpAdapter: adapterFetch(),
+
+ // mock interface response delay, in milliseconds
+ delay: 1000,
+
+ // Whether to print mock interface request information
+ mockRequestLogger: true,
+
+ // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova
+ // The following is the default callback function, which is suitable for requesting the adapter using adapterFetch
+ // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback
+ onMockResponse: data => new Response(JSON.stringify(data))
+});
+// highlight-end
+
+export const alovaInst = createAlova({
+ baseURL: 'http://xxx',
+
+ // Use the mock request adapter, if you need to switch adapters, please see the following practical suggestions
+ requestAdapter: mockAdapter,
+
+ statesHook: /** ... */
+});
+```
+
+### Paths match mode
+
+:::info version required
+
+1.5.0+
+
+:::
+
+By default, the path defined in `defineMock` is the full pathname of a url, see the following code snippet.
+
+```javascript
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org'
+ //...
+});
+alovaInst.Get('/user?id=1').send();
+```
+
+When the request path in the example is `https://api.alovajs.org/user?id=1`, its full pathname is `/user`, which can match `/user` in `defineMock`.
+
+Usually this is enough, but when your baseURL is not just a domain name.
+
+```javascript
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org/v1/subname'
+ //...
+});
+alovaInst.Get('/user?id=1').send();
+```
+
+In this example, the request path is `https://api.alovajs.org/v1/subname/user?id=1`, the matching path of the mock is `/v1/subname/user`, and `/ in the baseURL needs to be v1/subname` is also written together, which is slightly redundant when the number of interfaces is large.
+
+At this point, you can set `matchMode` to `methodurl` in `createAlovaMockAdapter`, it will only match the url defined in the method instance, for example, the above instance will match `/user?id=1` instead of The part in baseURL needs to be written. On the contrary, if the url in the method instance has a get parameter, it also needs to be written in the matching path of `defineMock`, just like `?id=1` here.
+
+```javascript
+createAlovaMockAdapter([mockGroup1 /** ... */], {
+ //...
+ // highlight-start
+ matchMode: 'methodurl'
+ // highlight-end
+});
+```
+
+## Practical advice
+
+### Group interfaces per developer per version
+
+In the team development scenario, we often only need to simulate some undeveloped interfaces for each version development, and use the test environment interface for the interface of the previous version. At this time, in order to achieve better simulation interface management, you can use The two dimensions, development version and developer, group interfaces.
+
+For example, there are two developers named _August_, _kevin_, they are developing v1.1 product features, they can manage the mock interface like this.
+
+```javascript title=August-v1.1.js
+import { defineMock } from '@alova/mock';
+
+export default defineMock({
+ '/todo': [
+ /** */
+ ],
+ '[POST]/todo': ({ data }) => {
+ // ...
+ // return...
+ }
+ // ...
+});
+```
+
+```javascript title=kevin-v1.1.js
+import { defineMock } from '@alova/mock';
+
+export default defineMock({
+ '[PUT]/todo/add': ({ data }) => {
+ // ...
+ // return...
+ },
+ '[DELETE]/todo/remove': ({ data }) => {
+ // ...
+ // return...
+ }
+ // ...
+});
+```
+
+```javascript title=request.js
+import Augustv1_1 from './August-v1.1';
+import Keevenv1_1 from './kevin-v1.1';
+
+const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], {
+ httpAdapter: adapterFetch(),
+ delay: 1000
+});
+export const alovaInst = createAlova({
+ baseURL: 'http://xxx',
+ requestAdapter: mockAdapter
+ // ...
+});
+```
+
+### Exclude mock code in production
+
+The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do:
+
+```javascript
+const alovaFetch = adapterFetch();
+const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], {
+ httpAdapter: alovaFetch,
+ delay: 1000,
+});
+
+export const alovaInst = createAlova({
+ baseURL: 'http://xxx',
+
+ // highlight-start
+ // In the production environment controlled by environment variables, the mock-related code will not be packaged in
+ requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : adapterFetch,
+ // highlight-end
+
+ statesHook: /** ... */
+});
+```
+
+### Use with mockjs
+
+If you don't want to write the mock data yourself, but use it with a mock js library (such as mockjs), you can do so.
+
+```javascript
+import { defineMock } from '@alova/mock';
+import Mock from 'mockjs';
+
+export default defineMock({
+ '/api1': Mock.mock({
+ 'id|1-10000': 100
+ })
+});
+```
+
+## Convert mock data
+
+**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `adapterFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format.
+
+### Convert response data
+
+You can intercept the mock response data in the `onMockResponse` field and return the transformed response data and response headers.
+
+> You can also throw an ERROR in onMockResponse to indicate a failure request.
+
+```javascript
+const mockAdapter = createAlovaMockAdapter(
+ [
+ /* mock data */
+ ],
+ {
+ //...
+ // highlight-start
+ onMockResponse(response, request, currentMethod) {
+ // response is the corresponding data set, which contains status, statusText, responseHeaders, body
+ // request is the request data, which contains query, params, headers, data
+ // currentMethod is the method instance of the current request
+ //...
+ // Return converted response data and response headers
+ return {
+ response: /** response data */,
+ headers: /** Response headers */
+ };
+ }
+ // highlight-end
+ }
+);
+```
+
+### Convert Error Instance
+
+You can intercept the error instance in the `onMockError` field and return the converted error message.
+
+> You can also throw an ERROR in onMockResponse to indicate failure request.
+
+```javascript
+const mockAdapter = createAlovaMockAdapter(
+ [
+ /* mock data */
+ ],
+ {
+ //...
+ // highlight-start
+ onMockError(error, currentMethod) {
+ // error is an error instance
+ // currentMethod is the method instance of the current request
+ //...
+ // Return the converted error message collection
+ }
+ // highlight-end
+ }
+);
+```
diff --git a/docs/resource/01-request-adapter/02-alova-adapter-xhr.md b/docs/resource/01-request-adapter/02-alova-adapter-xhr.md
new file mode 100644
index 000000000..3bacd286d
--- /dev/null
+++ b/docs/resource/01-request-adapter/02-alova-adapter-xhr.md
@@ -0,0 +1,336 @@
+---
+title: XMLHttpRequest Adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Install
+
+
+
+
+```bash
+npm install @alova/adapter-xhr@beta --save
+```
+
+
+
+
+```bash
+yarn add @alova/adapter-xhr@beta
+```
+
+
+
+
+## Instructions
+
+### create alova
+
+Use **xhrRequestAdapter** as request adapter for alova.
+
+```javascript
+import { createAlova } from 'alova';
+import { xhrRequestAdapter } from '@alova/adapter-xhr';
+
+const alovaInst = createAlova({
+ //...
+ requestAdapter: xhrResponseAdapter()
+ //...
+});
+```
+
+### Request
+
+The XMLHttpRequest adapter provides basic configuration parameters, including `responseType`, `withCredentials`, `mimeType`, `auth`, as follows:
+
+
+
+
+```html
+
+
Loading...
+
The request data is: {{ data }}
+
+
+
+```
+
+
+
+
+```jsx
+const list = () =>
+ alovaInst.Get('/list', {
+ /**
+ * Set the response data type
+ * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json" and "text"
+ * defaults to "json"
+ */
+ responseType: 'text',
+
+ /**
+ * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. Default is false
+ */
+ withCredentials: true,
+
+ /**
+ * Set the mimeType of the response data
+ */
+ mimeType: 'text/plain; charset=x-user-defined',
+
+ /**
+ * auth means use HTTP Basic authentication and provide credentials.
+ * This will set an `Authorization` header, overriding any existing
+ * Custom headers for `Authorization` set using `headers`.
+ * Note that only HTTP Basic authentication can be configured via this parameter.
+ * For Bearer tokens etc., use the `Authorization` custom header instead.
+ */
+ auth: {
+ username: 'name1',
+ password: '123456'
+ }
+ });
+
+const App = () => {
+ const { loading, data } = useRequest(list);
+
+ return (
+ { loading ?
+```
+
+
+
+
+### Upload
+
+Use `FormData` to upload files, and this `FormData` instance will be sent to the server through `xhr.send`. It will be set `Content-Type` automatically, you don't need to custom it with `multipart/form-data`.
+
+```javascript
+const uploadFile = imageFile => {
+ const formData = new FormData();
+ formData.append('file', imageFile);
+ return alovaInst.Post('/uploadImg', formData, {
+ // Start upload progress
+ enableUpload: true
+ });
+};
+
+const {
+ loading,
+ data,
+ uploading,
+ send: sendUpload
+} = useRequest(uploadFile, {
+ immediate: false
+});
+
+// Picture selection event callback
+const handleImageChoose = ({ target }) => {
+ sendUpload(target.files[0]);
+};
+```
+
+### download
+
+Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true.
+
+```javascript
+const downloadFile = () =>
+ alovaInst.Get('/bigImage.jpg', {
+ // Start download progress
+ enableDownload: true,
+ responseType: 'blob'
+ });
+
+const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, {
+ immediate: false
+});
+onSuccess(({ data: imageBlob }) => {
+ // download image
+ const anchor = document.createElement('a');
+ anchor.href = URL.createObjectURL(blob);
+ anchor.download = 'image.jpg';
+ anchor.click();
+ URL.revokeObjectURL(anchor.href);
+});
+const handleImageDownload = () => {
+ send();
+};
+```
+
+## Mock request adapter compatible
+
+When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/next/resource/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `alova/fetch` request adapter by default. When using the XMLHttpRequest adapter, we You need to adapt the response data of the mock request adapter to the XMLHttpRequest adapter. In this case, you need to use the `xhrMockResponse` exported in the **@alova/adapter-xhr** package as the response adapter.
+
+```javascript
+import { defineMock, createAlovaMockAdapter } from '@alova/mock';
+import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr';
+
+const mocks = defineMock({
+ //...
+});
+
+// mock data request adapter
+export default createAlovaMockAdapter([mocks], {
+ // After specifying the request adapter, requests that do not match the simulated interface will use this adapter to send requests
+ httpAdapter: xhrRequestAdapter(),
+
+ // Use xhrMockResponse to adapt the simulated data to the XMLHttpRequest adapter
+ onMockResponse: xhrMockResponse
+});
+
+export const alovaInst = createAlova({
+ //...
+ // Control whether to use the simulated request adapter through environment variables
+ requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter()
+});
+```
+
+## Typescript
+
+The XMLHttpRequest request adapter provides complete type adaptation.
+
+### method configuration
+
+When creating a method instance, in addition to the common configuration items in the method, you can also use the configuration items in `AlovaXHRRequestConfig`.
+
+```typescript
+/**
+ * xhr request configuration parameters
+ */
+interface AlovaXHRRequestConfig {
+ /**
+ * Set the response data type.
+ *
+ * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json", and "text".
+ * Setting 1: If the current global object is not a Window object, the setting to "document" is ignored.
+ * Setup 2: Throw an "InvalidStateError" DOMException if the state is loading or complete.
+ * Setting 3: Throws an "InvalidAccessError" DOMException if the sync flag is set and the current global object is a Window object.
+ * @default "json"
+ */
+ responseType?: XMLHttpRequestResponseType;
+
+ /**
+ * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. The default is false.
+ * An 'InvalidStateError' DOMException is thrown if the state is not sent or not opened, or if the send() flag is set.
+ * @default false
+ */
+ withCredentials?: boolean;
+
+ /**
+ * Set the mimeType of the response data
+ */
+ mimeType?: string;
+
+ /**
+ * `auth` indicates that HTTP Basic authentication should be used, and credentials are provided.
+ * This will set an `Authorization` header, overriding any existing
+ * Custom headers for `Authorization` set using `headers`.
+ * Note that only HTTP Basic authentication can be configured via this parameter.
+ * For Bearer tokens etc., use the `Authorization` custom header instead.
+ */
+ auth?: {
+ username: string;
+ password: string;
+ };
+}
+```
+
+### Response data
+
+XMLHttpRequest adapter response data is as follows:
+
+```typescript
+interface AlovaXHRResponseHeaders {
+ [x: string]: any;
+}
+interface AlovaXHRResponse {
+ status: number;
+ statusText: string;
+ data: T;
+ headers: AlovaXHRResponseHeaders;
+}
+```
diff --git a/docs/resource/01-request-adapter/03-alova-adapter-axios.md b/docs/resource/01-request-adapter/03-alova-adapter-axios.md
new file mode 100644
index 000000000..3d3cae9ae
--- /dev/null
+++ b/docs/resource/01-request-adapter/03-alova-adapter-axios.md
@@ -0,0 +1,292 @@
+---
+title: axios adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Install
+
+
+
+
+```bash
+npm install @alova/adapter-axios@beta --save
+```
+
+
+
+
+```bash
+yarn add @alova/adapter-axios@beta
+```
+
+
+
+
+## Instructions
+
+### create alova
+
+Use **axiosRequestAdapter** as request adapter for alova.
+
+```javascript
+import { createAlova } from 'alova';
+import { axiosRequestAdapter } from '@alova/adapter-axios';
+
+const alovaInst = createAlova({
+ //...
+ requestAdapter: axiosRequestAdapter()
+ //...
+});
+```
+
+The adapter will use the default axios instance to make requests internally. If you set some global parameters for axios, you may need to pay attention to the following two points:
+
+1. Priority is given to using the `baseURL` and `timeout` parameters in the axios instance, so if you set these parameters on the axios instance, you do not need to set them in `createAlova`;
+2. The `beforeRequest` hook of the alova instance will be triggered earlier than the `interceptor.request` of the axios instance, and the `responded` hook of the alova will be triggered later than the `interceptor.response` of the axios instance;
+
+> You can also [use custom axios instance](#use-custom-axios-instance)
+
+## usage
+
+### request
+
+The usage of request is exactly the same as that used in the web environment. Already fully compatible with **axios**, you can specify [all configuration items](https://axios-http.com/docs/req_config) supported by `axios` in _config_ of method instance creation.
+
+
+
+
+```html
+
+
Loading...
+
The request data is: {{ data }}
+
+
+
+```
+
+
+
+
+```jsx
+const list = () =>
+ alovaInst.Get('/list', {
+ // The set parameters will be passed to axios
+ paramsSerializer: params => {
+ return Qs.stringify(params, {arrayFormat: 'brackets'})
+ },
+ });
+
+const App = () => {
+ const { loading, data } = useRequest(list);
+
+ return (
+ { loading ?
+```
+
+
+
+
+### Upload
+
+Use `FormData` to upload files, and this `FormData` instance will be passed to axios, which is consistent with the usage of axios upload files.
+
+```javascript
+const uploadFile = imageFile => {
+ const formData = new FormData();
+ formData.append('file', imageFile);
+ return alovaInst.Post('/uploadImg', formData, {
+ // Start upload progress
+ enableUpload: true
+ });
+};
+
+const {
+ loading,
+ data,
+ uploading,
+ send: sendUpload
+} = useRequest(uploadFile, {
+ immediate: false
+});
+
+// Picture selection event callback
+const handleImageChoose = ({ target }) => {
+ sendUpload(target.files[0]);
+};
+```
+
+### download
+
+Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true.
+
+```javascript
+const downloadFile = () =>
+ alovaInst.Get('/bigImage.jpg', {
+ // Start download progress
+ enableDownload: true,
+ responseType: 'blob'
+ });
+
+const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, {
+ immediate: false
+});
+onSuccess(({ data: imageBlob }) => {
+ // download image
+ const anchor = document.createElement('a');
+ anchor.href = URL.createObjectURL(blob);
+ anchor.download = 'image.jpg';
+ anchor.click();
+ URL.revokeObjectURL(anchor.href);
+});
+const handleImageDownload = () => {
+ send();
+};
+```
+
+## Use custom axios instance
+
+By default, this adapter will use the default axios instance for requests, but in some cases you need to use a custom created axios instance. You can do this:
+
+```javascript
+const customAxios = axios.create({
+ // ...
+});
+
+const alovaInst = createAlova({
+ // ...
+ // highlight-start
+ requestAdapter: axiosRequestAdapter({
+ axios: customAxios
+ })
+ // highlight-end
+});
+```
+
+## Mock request adapter compatible
+
+When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/next/resource/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `alova/fetch` request adapter by default. When using the axios adapter, we The response data of the mock request adapter needs to be compatible with **AxiosResponse**, and the error instance is **AxiosError**, so you need to use `axiosMockResponse` exported from the **@alova/adapter-axios** package as the response adapter .
+
+```javascript
+import { defineMock, createAlovaMockAdapter } from '@alova/mock';
+import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios';
+
+const mocks = defineMock({
+ //...
+});
+
+// mock data request adapter
+export default createAlovaMockAdapter([mocks], {
+ // After specifying axios request adapter, requests that do not match the simulated interface will use this adapter to send requests
+ httpAdapter: axiosRequestAdapter(),
+
+ // axiosMockResponse contains onMockResponse and onMockError
+ // Used to convert mock data to AxiosResponse and AxiosError compatible format
+ ...axiosMockResponse
+});
+
+export const alovaInst = createAlova({
+ //...
+ // Control whether to use the simulated request adapter through environment variables
+ requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter()
+});
+```
+
+##Typescript
+
+The axios request adapter provides complete type adaptation. The type of method configuration and response data will exactly match the type of axios.
+
+### method configuration
+
+When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `AxiosRequestConfig`, we have removed the items that conflict with the general configuration of the method instance in the type.
+
+```typescript
+/**
+ * axios request configuration parameters
+ * Removed the attributes that conflicted with the method
+ */
+export type AlovaAxiosRequestConfig = Omit<
+ AxiosRequestConfig,
+ | 'url'
+ | 'method'
+ | 'baseURL'
+ | 'headers'
+ | 'params'
+ | 'data'
+ | 'timeout'
+ | 'cancelToken'
+ | 'signal'
+ | 'onUploadProgress'
+ | 'onDownloadProgress'
+>;
+```
+
+### Response data
+
+The response data type of axios is `AxiosResponse`, when you use the axios adapter, you will also get the response data in the same format. In actual use, we usually need to process response data globally. A simple example is as follows:
+
+```typescript
+const alovaInst = createAlova(
+ baseURL: 'https://api.alovajs.org',
+ requestAdapter: axiosRequestAdapter(),
+ responded(response) {
+ // response is automatically inferred as AxiosResponse type
+ return response.data;
+ }
+});
+```
+
+### Error
+
+When axios receives non-20x and 30x response status codes, it will throw an error. In order to contain more information, axios custom-designed the error instance into an `AxiosError` instance instead of a normal Error instance, so when encountering An error will be thrown when there is a server error or a network error, and you can catch it in the global error callback.
+
+```typescript
+const alovaInst = createAlova(
+ baseURL: 'https://api.alovajs.org',
+ requestAdapter: axiosRequestAdapter(),
+ responded: {
+ onSuccess(response) {
+ // response is automatically inferred as AxiosResponse type
+ return response.data;
+ },
+ onError(err: AxiosError) {
+ // err type is any by default, you can cast it to AxiosError
+ //...
+ }
+ }
+});
+```
diff --git a/docs/resource/01-request-adapter/04-alova-adapter-taro.md b/docs/resource/01-request-adapter/04-alova-adapter-taro.md
new file mode 100644
index 000000000..995938fd7
--- /dev/null
+++ b/docs/resource/01-request-adapter/04-alova-adapter-taro.md
@@ -0,0 +1,443 @@
+---
+title: Taro Adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info Tips
+
+This plugin only supports the taro application of react 16.8+, vue3 version.
+
+:::
+
+## Install
+
+
+
+
+```bash
+npm install @alova/adapter-taro@beta --save
+```
+
+
+
+
+```bash
+yarn add @alova/adapter-taro@beta
+```
+
+
+
+
+:::warning React-Native Application
+
+If you are develop a React-Native app with Taro, please ensure `metro >= 0.76.0` and enable `resolver.unstable_enablePackageExports` in the `metro.config.js`.
+
+[about unstable_enablePackageExports of metro](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental)
+
+:::
+
+:::warning Dependency precompilation issues
+
+[Dependency precompilation function](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91) has been added in Taro v3.5 beta, and is enabled by default in development mode when you are using the `alova` library and `@alova/scene-react(vue)` may cause the error `` [alova]can not call useHooks until set the `statesHook` at alova instance. ``. This is caused by the prebundle feature repeatedly packaging two different `alova` packages. , turning off the prebundle function at this time can solve this problem.
+
+```js
+// config/dev.ts
+export default {
+ // ...
+ compiler: {
+ type: 'webpack5',
+ prebundle: {
+ // Close prebundle
+ enable: false
+ }
+ }
+} satisfies UserConfigExport
+
+```
+
+Thanks to [LBinin’s issue](https://github.com/alovajs/scene/issues/63).
+
+This problem has been committed to the Taro team [issue](https://github.com/NervJS/taro/issues/15728) and we look forward to solving this issue.
+
+:::
+
+## Usage
+
+### create alova
+
+Calling **AdapterTaro** will return _Request Adapter_, _Storage Adapter_, and _ReactHook_, so you no longer need to set these three items, and the usage is exactly the same.
+
+
+
+
+```javascript
+import { createAlova } from 'alova';
+import AdapterTaro from '@alova/adapter-taro';
+
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterTaro()
+});
+```
+
+
+
+
+
+```javascript
+import { createAlova } from 'alova';
+import AdapterTaroVue from '@alova/adapter-taro/vue';
+
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterTaroVue()
+});
+```
+
+
+
+
+### Request
+
+The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `Taro.request`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/request/) supported by `Taro.request` in the _config_ of method instance creation
+
+
+
+
+```jsx
+const list = () =>
+ alovaInst.Get('/list', {
+ // The set parameters will be passed to Taro.request
+ enableHttp2: true
+ });
+
+const App = () => {
+ const { loading, data } = useRequest(list);
+
+ return (
+ { loading ? Loading... : null }
+ The requested data is: { JSON.stringify(data) }
+ )
+};
+```
+
+
+
+
+```html
+
+ Loading...
+ The requested data is: {{ data }}
+
+
+
+```
+
+
+
+
+### Upload
+
+When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `Taro.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name` and `filePath`, these two parameters will be passed to `Taro.uploadFile`, at the same time, you can also specify other parameters in data, and the request adapter will pass them to `formData `in parameters.
+
+Similarly, it is fully compatible with `Taro.uploadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile) supported by `Taro.uploadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance.
+
+
+
+
+```jsx
+const uploadFile = (name, filePath, formData) =>
+ alovaInst.Post(
+ '/uploadImg',
+ {
+ name,
+ filePath,
+
+ // Additional data will be passed into formData of uni.uploadFile
+ ...formData
+ },
+ {
+ // Set the request method to upload, and the adapter will call uni.uploadFile
+ requestType: 'upload',
+
+ // Start upload progress
+ enableUpload: true
+ }
+ );
+
+const App = () => {
+ const { loading, data, uploading, send } = useRequest(uploadFile, {
+ immediate: false
+ });
+
+ const handleImageChoose = () => {
+ Taro.chooseImage({
+ success: chooseImageRes => {
+ const tempFilePaths = chooseImageRes.tempFilePaths;
+ send('fileName', tempFilePaths[0], {
+ extra1: 'a',
+ extra2: 'b'
+ });
+ }
+ });
+ };
+
+ return (
+ { loading ? Uploading... : null }
+ Upload progress: { uploading.loaded }/{ uploading.total }
+
+ {/* ... */}
+ )
+}
+```
+
+
+
+
+```html
+
+ Uploading...
+ Upload progress: {{ uploading.loaded }}/{{ uploading.total }}
+
+
+
+
+
+```
+
+
+
+
+### download
+
+When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `Taro.downloadFile`.
+
+Similarly, it is fully compatible with `Taro.downloadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/download/downloadFile) supported by `Taro.downloadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance.
+
+
+
+
+```jsx
+const downloadFile = filePath =>
+ alovaInst.Get('/bigImage.jpg', {
+ // Set the request method to download, and the adapter will call uni.downloadFile
+ requestType: 'download',
+ filePath,
+
+ // Start download progress
+ enableDownload: true
+ });
+
+const App = () => {
+ const { loading, data, downloading, send } = useRequest(downloadFile, {
+ immediate: false
+ });
+ const handleImageDownload = () => {
+ send('file_save_path');
+ };
+
+ return (
+ { loading ? Downloading... : null }
+ Download progress: { downloading.loaded }/{ downloading.total }
+
+ {/* ... */}
+ );
+}
+```
+
+
+
+
+```html
+
+ Downloading...
+ Download progress: {{ downloading.loaded }}/{{ downloading.total }}
+
+
+
+
+
+```
+
+
+
+
+## Mock request adapter compatible
+
+When using Taro to develop applications, we may still need to use mock requests, but by default, the response data of [Mock Request Adapter (@alova/mock)](/next/resource/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `alova/fetch` request adapter by default. When used in the Taro environment, we need to make the response data of the simulated request adapter compatible with the Taro adapter, so you need to use the **@alova/adapter-taro** package exported `taroMockResponse` as response adapter.
+
+```javascript
+import { defineMock, createAlovaMockAdapter } from '@alova/mock';
+import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro';
+
+const mocks = defineMock({
+ //...
+});
+
+// mock data request adapter
+export default createAlovaMockAdapter([mocks], {
+ // After specifying the taro request adapter, requests that do not match the simulated interface will use this adapter to send requests
+ httpAdapter: taroRequestAdapter,
+
+ // Simulate the response adapter, after specifying, the response data will be converted to a taro-compatible data format
+ onMockResponse: taroMockResponse
+});
+
+export const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ timeout: 5000,
+ ...AdapterTaro({
+ // Control whether to use the simulated request adapter through environment variables
+ mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined
+ })
+ //...
+});
+```
+
+## Typescript
+
+The taro request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of taro.
+
+### method configuration
+
+When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, we have removed the type and method Items that conflict with the common configuration of the instance.
+
+```typescript
+/**
+ * Taro.request requests additional parameters
+ */
+export type TaroRequestConfig = Omit<
+ Taro.request.Option,
+ 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete'
+>;
+
+/**
+ * Taro.uploadFile additional parameter
+ */
+export type TaroUploadConfig = Omit<
+ Taro.uploadFile.Option,
+ | 'url'
+ | 'filePath'
+ | 'name'
+ | 'header'
+ | 'formData'
+ | 'timeout'
+ | 'success'
+ | 'fail'
+ | 'complete'
+>;
+
+/**
+ * Taro.downloadFile additional parameters
+ */
+export type TaroDownloadConfig = Omit<
+ Taro.downloadFile.Option,
+ 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete'
+>;
+
+/**
+ * Merged request configuration parameters
+ */
+export type TaroConfig = {
+ /**
+ * Request type, upload means upload, download means download, not filling means normal request
+ */
+ requestType?: 'upload' | 'download';
+} & TaroRequestConfig &
+ TaroUploadConfig &
+ TaroDownloadConfig;
+```
+
+### Response data
+
+Because the taro request adapter is compatible with `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, but their response value types are slightly different, so the response data type is as follows:
+
+```typescript
+type TaroResponse =
+ // The response type of Taro.request
+ | Taro.request.SuccessCallbackResult
+
+ // The response type of Taro.uploadFile
+ | Taro.uploadFile.SuccessCallbackResult
+
+ // The response type of Taro.downloadFile
+ | Taro.downloadFile.FileSuccessCallbackResult;
+```
+
+In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows:
+
+```typescript
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterTaro(),
+ responded(response) {
+ const { statusCode, data } = response as Taro.request.SuccessCallbackResult;
+ if (statusCode >= 400) {
+ throw new Error('request error');
+ }
+ return data || null;
+ }
+});
+```
diff --git a/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md b/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md
new file mode 100644
index 000000000..365c42430
--- /dev/null
+++ b/docs/resource/01-request-adapter/05-alova-adapter-uniapp.md
@@ -0,0 +1,278 @@
+---
+title: Uniapp Adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info Tips
+
+This plugin only supports vue3 version of uniapp application.
+
+:::
+
+## Install
+
+
+
+
+```bash
+npm install @alova/adapter-uniapp@beta --save
+```
+
+
+
+
+```bash
+yarn add @alova/adapter-uniapp@beta
+```
+
+
+
+
+## Usage
+
+### create alova
+
+Calling **AdapterUniapp** will return _Request Adapter_, _Storage Adapter_, and _VueHook_, so you no longer need to set these three items, and the usage is exactly the same.
+
+```javascript
+import { createAlova } from 'alova';
+import AdapterUniapp from '@alova/adapter-uniapp';
+
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterUniapp()
+});
+```
+
+### Request
+
+The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `uni.request`, you can specify [all configuration items] supported by `uni.request` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api/request/ request.html)
+
+```html
+
+ Loading...
+ The requested data is: {{ data }}
+
+
+
+```
+
+When using `useRequest/useWatcher` to send a request immediately, it will be executed asynchronously in the `onLoad` hook, so you can access the options data in `methodHandler` as follows:
+
+```javascript
+import { onLoad } from '@dcloudio/uni-app';
+
+let options = {};
+onLoad(opt => {
+ options = opt;
+});
+const { loading, data } = useRequest(() => getDetail(options.id));
+```
+
+### Upload
+
+When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `uni.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name`, `filePath or files`, and `file` (if necessary), these 4 parameters will be passed to `uni.uploadFile`, at the same time, you can also specify other parameters besides these 4 parameters in data , the request adapter will pass them into the `formData` parameter.
+
+Similarly, it is fully compatible with `uni.uploadFile`, you can specify [all configuration items] supported by `uni.uploadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#uploadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance.
+
+```html
+
+ Uploading...
+ Upload progress: {{ uploading.loaded }}/{{ uploading.total }}
+
+
+
+
+
+```
+
+### download
+
+When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `uni.downloadFile`.
+
+Similarly, it is fully compatible with `uni.downloadFile`, you can specify [all configuration items] supported by `uni.downloadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#downloadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance.
+
+```html
+
+ Downloading...
+ Download progress: {{ downloading.loaded }}/{{ downloading.total }}
+
+
+
+
+
+```
+
+## Mock request adapter compatible
+
+When using uniapp to develop applications, we may still need to use simulated requests, but by default, the response data of [mock adapter (@alova/mock)](/next/resource/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `alova/fetch` request adapter by default. When used in the uniapp environment, we need to make the response data of the simulated request adapter compatible with the uniapp adapter, so you need to use the **@alova/adapter-uniapp** package exported `uniappMockResponse` as response adapter.
+
+```javascript
+import { defineMock, createAlovaMockAdapter } from '@alova/mock';
+import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp';
+
+const mocks = defineMock({
+ //...
+});
+
+// mock data request adapter
+export default createAlovaMockAdapter([mocks], {
+ // After specifying the uniapp request adapter, requests that do not match the simulated interface will use this adapter to send requests
+ httpAdapter: uniappRequestAdapter,
+
+ // Simulate the response adapter, after specifying, the response data will be converted to a uniapp-compatible data format
+ onMockResponse: uniappMockResponse
+});
+
+export const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ timeout: 5000,
+ ...AdapterUniapp({
+ // Control whether to use the simulated request adapter through environment variables
+ mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined
+ })
+ //...
+});
+```
+
+## Typescript
+
+uniapp request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of uniapp.
+
+### method configuration
+
+When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, we have removed and method from the type Items that conflict with the common configuration of the instance.
+
+```typescript
+/**
+ * uni.request requests additional parameters
+ */
+export type UniappRequestConfig = Omit<
+ UniNamespace.RequestOptions,
+ 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete'
+>;
+
+/**
+ * uni.uploadFile additional parameters
+ */
+export type UniappUploadConfig = Omit<
+ UniNamespace.UploadFileOption,
+ 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete'
+>;
+
+/**
+ * uni.downloadFile additional parameters
+ */
+export type UniappDownloadConfig = Omit<
+ UniNamespace.DownloadFileOption,
+ 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete'
+>;
+
+/**
+ * Merged request configuration parameters
+ */
+export type UniappConfig = {
+ /**
+ * Request type, upload means upload, download means download, not filling means normal request
+ */
+ requestType?: 'upload' | 'download';
+} & UniappRequestConfig &
+ UniappUploadConfig &
+ UniappDownloadConfig;
+```
+
+### Response data
+
+Because the uniapp request adapter is compatible with `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, but their response value types are slightly different, so the response data type is as follows:
+
+```typescript
+type UniappResponse =
+ // The response type of uni.request
+ | UniNamespace.RequestSuccessCallbackResult
+
+ // The response type of uni.uploadFile
+ | UniNamespace.UploadFileSuccessCallbackResult
+
+ // The response type of uni.downloadFile
+ | UniNamespace.DownloadSuccessData;
+```
+
+In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows:
+
+```typescript
+const alovaInst = createAlova(
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterUniapp(),
+ responded(response) {
+ const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult;
+ if (statusCode >= 400) {
+ throw new Error('request error');
+ }
+ return data || null;
+ }
+});
+```
diff --git a/docs/resource/01-request-adapter/README.md b/docs/resource/01-request-adapter/README.md
new file mode 100644
index 000000000..f50570493
--- /dev/null
+++ b/docs/resource/01-request-adapter/README.md
@@ -0,0 +1,9 @@
+---
+title: Request Adapter
+---
+
+import DocCardList from '@theme/DocCardList';
+
+We provide a variety of request adapters.
+
+
diff --git a/docs/resource/01-request-adapter/_category_.json b/docs/resource/01-request-adapter/_category_.json
new file mode 100644
index 000000000..323e503e1
--- /dev/null
+++ b/docs/resource/01-request-adapter/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Request Adapter"
+}
diff --git a/docs/resource/02-storage-adapter/01-psc.md b/docs/resource/02-storage-adapter/01-psc.md
new file mode 100644
index 000000000..f4659fd39
--- /dev/null
+++ b/docs/resource/02-storage-adapter/01-psc.md
@@ -0,0 +1,225 @@
+---
+title: Process Shared Adapter
+---
+
+Process shared storage adapter allows you to share cache in multi-process environment.
+
+We implemented two adapters that can be used in Node.js and Electron.
+
+:::info Tips
+
+Only supports Alova 3.0 and above.
+
+:::
+
+## Install
+
+```bash
+npm install @alova/psc@beta --save
+```
+
+## Use in Node.js
+
+In Node.js environment, you can use `NodeSyncAdapter` and `createNodeSharedCacheSynchronizer` to synchronize the cache between child processes.
+
+1. Set up the synchronizer in the master process:
+
+```javascript
+const process = require('node:process');
+const cluster = require('cluster');
+const { createPSCSynchronizer, NodeSyncAdapter } = require('@alova/psc');
+
+if (cluster.isMaster) {
+ // highlight-start
+ // Make sure to call this before creating child processes
+ const stopIPC = await createPSCSynchronizer(NodeSyncAdapter());
+ // highlight-end
+
+ process.on('SIGTERM', stopIPC);
+} else {
+ // fork worker processes
+}
+```
+
+2. Use the adapter in the child process:
+
+```javascript
+const process = require('node:process');
+const { createPSCAdapter, NodeSyncAdapter, ExplictCacheAdapter } = require('@alova/psc');
+
+const pscAdapter = createPSCAdapter(
+ NodeSyncAdapter(stopIPC => {
+ process.on('SIGTERM', stopIPC);
+ })
+);
+createAlova({
+ // ...
+ l1Cache: pscAdapter
+});
+```
+
+### Sharing Multiple Caches
+
+Of course, you can share both l1Cache and l2Cache at the same time! Just use the scope option and create different shared storage adapters.
+
+```javascript
+const createScopedPSCAdapter = (scope: string) =>
+ createPSCAdapter(
+ NodeSyncAdapter(stopIPC => {
+ process.on('SIGTERM', stopIPC);
+ }),
+ // This parameter is used to specify the storage adapter. We will introduce this later
+ undefined,
+ // highlight-start
+ { scope }
+ // highlight-end
+ );
+```
+
+## Use in Electron
+
+In the Electron environment, use ElectronSyncAdapter and createPSCSynchronizer to synchronize the cache between renderer processes.
+
+1. Set up the synchronizer in the main process:
+
+```javascript
+// main.js
+import { createPSCSynchronizer, ElectronSyncAdapter } from '@alova/psc';
+import { ipcMain } from 'electron';
+
+// Initialize the synchronizer
+createPSCSynchronizer(ElectronSyncAdapter(ipcMain));
+
+// ...other codes
+```
+
+2. Use the adapter in the renderer process and expose it to the global object:
+
+```javascript
+// payload.js
+import { createPSCAdapter, ElectronSyncAdapter } from '@alova/psc';
+import { ipcRenderer, contextBridge } from 'electron';
+
+const pscAdapter = createPSCAdapter(ElectronSyncAdapter(ipcRenderer));
+
+contextBridge.exposeInMainWorld('pscAdapter', pscAdapter);
+```
+
+3. Use it when creating the Alova instance:
+
+```javascript
+import { createAlova } from 'alova';
+
+const alova = createAlova({
+ // ...
+ // highlight-start
+ l1Cache: window.pscAdapter
+ // highlight-end
+});
+```
+
+If you use TypeScript, don't forget to add the global type declaration:
+
+```typescript
+// env.d.ts
+declare global {
+ interface Window {
+ // ...
+ // highlight-start
+ pscAdapter: import('@alova/psc').SyncAdapter;
+ // highlight-end
+ }
+}
+```
+
+:::warning note
+
+When using multiple Alova instances, there is no need to create multiple `PSCAdapter` objects for them. Alova instances are identified by different IDs, so the same channel can be safely reused.
+
+:::
+
+## Custom Storage Adapter
+
+By passing the second parameter to createPSCAdapter, you can specify the storage adapter to use.
+
+```typescript
+const pscAdapter = createPSCAdapter(
+ ElectronSyncAdapter(
+ ipcRenderer,
+ // highlight-start
+ // Same as passing to l1Cache in createAlova. Use default implementation if passing undefined.
+ MyStorageAdapter()
+ // highlight-end
+ )
+);
+
+createAlova({
+ // ...
+ l1Cache: pscAdapter
+});
+```
+
+> You can also use [lru-cache](https://www.npmjs.com/package/lru-cache) as a cache adapter.
+
+## Custom SharedAdapter
+
+If you want to implement your own process shared adapter, you need to:
+
+1. Determine the communication method between processes.
+2. Implement the `PSCSyncAdapter` interface:
+
+In detail, the first step is to determine the bidirectional communication method between the main process and the child process. For example, in Node.js, alova uses `node-ipc` to implement communication between the main process and the child process. In Electron, we use `ipcRenderer` and `ipcMain`.
+
+Next, implement the `SyncAdapter` interface for both the main process and the child process. Use `createSyncAdapter` for type assistance.
+
+Here is an implementation in Electron:
+
+```typescript
+import { createPSCSynchronizer, createSyncAdapter } from '@/sharedCacheAdapter';
+import type { IpcMain, IpcRenderer } from 'electron';
+
+const EventName = {
+ TO_MAIN: 'alova-ipc-to-main',
+ TO_CLIENT: 'alova-ipc-to-client'
+} as const;
+
+/**
+ * Use this function in payload.js/
+ */
+export function MyElectronSyncAdapter(ipcRenderer: IpcRenderer) {
+ // createSyncAdapter is a helper to implement SyncAdapter. do nothing
+ return createSyncAdapter({
+ send(event) {
+ ipcRenderer.emit(EventName.TO_MAIN, event);
+ },
+ receive(handler) {
+ ipcRenderer.on(EventName.TO_CLIENT, (_, payload) => handler(payload));
+ }
+ });
+}
+
+let hasSynchronizer = false;
+
+/**
+ * Use this function in main process.
+ */
+export function createMyElectronSharedCacheSynchronizer(ipcMain: IpcMain) {
+ if (hasSynchronizer) {
+ return;
+ }
+ hasSynchronizer = true;
+
+ createPSCSynchronizer(
+ createSyncAdapter({
+ send(event) {
+ ipcMain.emit(EventName.TO_CLIENT, event);
+ },
+ receive(handler) {
+ ipcMain.on(EventName.TO_MAIN, (_, payload) => handler(payload));
+ }
+ })
+ );
+}
+```
+
+And refer the example above.
diff --git a/docs/resource/02-storage-adapter/README.md b/docs/resource/02-storage-adapter/README.md
new file mode 100644
index 000000000..2bdc97622
--- /dev/null
+++ b/docs/resource/02-storage-adapter/README.md
@@ -0,0 +1,7 @@
+---
+title: 存储适配器
+---
+
+import DocCardList from '@theme/DocCardList';
+
+
diff --git a/docs/resource/02-storage-adapter/_category_.json b/docs/resource/02-storage-adapter/_category_.json
new file mode 100644
index 000000000..0d4ccc0c3
--- /dev/null
+++ b/docs/resource/02-storage-adapter/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Storage Adapter"
+}
diff --git a/docs/resource/03-framework/01-vue-composition.md b/docs/resource/03-framework/01-vue-composition.md
new file mode 100644
index 000000000..dd8b5aed5
--- /dev/null
+++ b/docs/resource/03-framework/01-vue-composition.md
@@ -0,0 +1,27 @@
+---
+title: vue composition
+---
+
+Support request strategies in vue composition through `alova/vue`.
+
+```js
+import { createAlova } from 'alova';
+import VueHook from 'alova/vue';
+
+const alovaInstance = createAlova({
+ // ...
+ statesHook: VueHook
+});
+```
+
+If you use composition syntax in vue2, please use `alova/vue-demi`.
+
+```js
+import { createAlova } from 'alova';
+import VueHook from 'alova/vue-demi';
+
+const alovaInstance = createAlova({
+ // ...
+ statesHook: VueHook
+});
+```
diff --git a/docs/resource/03-framework/02-react.md b/docs/resource/03-framework/02-react.md
new file mode 100644
index 000000000..4e78e0955
--- /dev/null
+++ b/docs/resource/03-framework/02-react.md
@@ -0,0 +1,15 @@
+---
+title: react
+---
+
+Support request strategies in react through `alova/react`.
+
+```js
+import { createAlova } from 'alova';
+import ReactHook from 'alova/react';
+
+const alovaInstance = createAlova({
+ // ...
+ statesHook: ReactHook
+});
+```
diff --git a/docs/resource/03-framework/03-svelte.md b/docs/resource/03-framework/03-svelte.md
new file mode 100644
index 000000000..de775795f
--- /dev/null
+++ b/docs/resource/03-framework/03-svelte.md
@@ -0,0 +1,15 @@
+---
+title: svelte
+---
+
+Support request strategies in svelte through `alova/svelte`.
+
+```js
+import { createAlova } from 'alova';
+import SvelteHook from 'alova/svelte';
+
+const alovaInstance = createAlova({
+ // ...
+ statesHook: SvelteHook
+});
+```
diff --git a/docs/resource/03-framework/04-solid.md b/docs/resource/03-framework/04-solid.md
new file mode 100644
index 000000000..eb5fb12f2
--- /dev/null
+++ b/docs/resource/03-framework/04-solid.md
@@ -0,0 +1,5 @@
+---
+title: solid
+---
+
+Comming soon...
diff --git a/docs/resource/03-framework/05-angular.md b/docs/resource/03-framework/05-angular.md
new file mode 100644
index 000000000..dfc05e123
--- /dev/null
+++ b/docs/resource/03-framework/05-angular.md
@@ -0,0 +1,5 @@
+---
+title: angular
+---
+
+Comming soon...
diff --git a/docs/resource/03-framework/06-preact.md b/docs/resource/03-framework/06-preact.md
new file mode 100644
index 000000000..a996552a5
--- /dev/null
+++ b/docs/resource/03-framework/06-preact.md
@@ -0,0 +1,5 @@
+---
+title: preact
+---
+
+Comming soon...
diff --git a/docs/resource/03-framework/07-vue-options.md b/docs/resource/03-framework/07-vue-options.md
new file mode 100644
index 000000000..884be7b36
--- /dev/null
+++ b/docs/resource/03-framework/07-vue-options.md
@@ -0,0 +1,222 @@
+---
+title: vue options
+---
+
+import Tabs from '@theme/Tabs';
+
+import TabItem from '@theme/TabItem';
+
+Usually, use hook can only be used in vue's setup, but through the auxiliary function provided by `@alova/vue-options`, you can also use alova's use hook in vue's options, which is perfectly compatible with almost all functions of alova. You can use all request strategies of `alova/client` under options.
+
+> It can be used in both vue2 and vue3.
+
+[![npm](https://img.shields.io/npm/v/@alova/vue-options)](https://www.npmjs.com/package/@alova/vue-options)
+[![build](https://github.com/alovajs/vue-options/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/alovajs/vue-options/actions/workflows/release.yml)
+[![coverage status](https://coveralls.io/repos/github/alovajs/vue-options/badge.svg?branch=main)](https://coveralls.io/github/alovajs/vue-options?branch=main)
+![typescript](https://badgen.net/badge/icon/typescript?icon=typescript&label)
+![license](https://img.shields.io/badge/license-MIT-blue.svg)
+
+## Installation
+
+
+
+
+```bash
+npm install alova @alova/vue-options@beta --save
+```
+
+
+
+
+```bash
+yarn add alova @alova/vue-options@beta
+```
+
+
+
+
+:::info Version requirements
+
+alova version >= 3.0.0
+
+vue version >= 2.17.0
+
+:::
+
+## Usage
+
+### Map hook status and functions to vue instance
+
+First use `vueOptionHook` to create an alova instance.
+
+```javascript
+import { createAlova, Method } from 'alova';
+import adapterFetch from 'alova/fetch';
+import { VueOptionsHook } from '@alova/vue-options';
+
+// api.js
+const alovaInst = createAlova({
+ baseURL: 'http://example.com',
+ statesHook: VueOptionsHook,
+ requestAdapter: adapterFetch(),
+ responded: response => response.json()
+});
+
+/** @type {() => Method} */
+export const getData = () => alovaInst.Get('/todolist');
+```
+
+Use `mapAlovaHook` to map the return value collection of use hook to the component instance. Here is how to access the responsive state and operation function:
+
+1. You can use the key of the collection To access responsive states such as `loading/data/error`, such as `this.key.loading`, `this.key.data`.
+2. You can access the operation function by adding the function name to the collection key and concatenating it with `$`, such as `this.key$send`, `this.key$onSuccess`.
+
+The following is a complete example.
+
+```html
+
+
+
+
+
+```
+
+### Computed properties
+
+If you need to define a computed property that depends on the request status related to the hook, just write it as usual.
+
+```javascript
+export default {
+ computed: {
+ todoRequestLoading() {
+ return this.todoRequest.loading;
+ },
+ todoRequestData() {
+ return this.todoRequest.data;
+ }
+ }
+};
+```
+
+### Listening to hook status changes
+
+The usage of monitoring status is also consistent with vue.
+
+```javascript
+export default {
+ watch: {
+ 'todoRequest.loading'(newVal, oldVal) {
+ // ...
+ }
+ }
+};
+```
+
+## Function description
+
+### mapAlovaHook
+
+`mapAlovaHook` is used to map the state and function set returned by alova's use hook to the vue component instance through mixins. It receives a callback function and returns the return value collection of the use hook.
+
+It is worth noting that the callback function will be executed in the `created` phase, and you can access the vue component instance in the following way.
+
+```javascript
+// 1. Access the component instance through this. Note that the callback function cannot be an arrow function
+mapAlovaHook(function () {
+ console.log(this);
+ return {
+ todoRequest: useRequest(getData)
+ };
+});
+
+// =======================
+// 2. Access the component instance through function parameters. You can use arrow functions at this time
+mapAlovaHook(vm => {
+ console.log(vm);
+ return {
+ todoRequest: useRequest(getData)
+ };
+});
+```
+
+## Type support
+
+### Automatic inference
+
+`@alova/vue-options` provides complete ts type support. Regardless of whether typescript is used, the mapped value type will be automatically inferred, for example:
+
+```javascript
+this.todoRequest.loading; // boolean
+this.todoRequest.error; // Error | undefined
+this.todoRequest.data; // any
+this.todoRequest$send; // (...args: any[]) => Promise
+this.todoRequest$onSuccess; // (handler: SuccessHandler) => void
+this.todoRequest$onError; // (handler: ErrorHandler) => void
+this.todoRequest$onComplete; // (handler: CompleteHandler) => void
+// ...
+```
+
+### Add types to response data
+
+Except for `this.todoRequest.data`, other values have the correct type, so how to set the type for `data`? In fact, it is the same as alova in other environments.
+
+**javascript**
+
+In javascript, you can use type annotations to add types. The first two generic parameters of Method are `unknown`, and the third generic parameter is the type of the response data
+
+```javascript
+import { Method } from 'alova';
+
+/** @type {() => Method} */
+export const getData = () => alovaInst.Get('/todolist');
+```
+
+**typescript**Add response data types in typescript, please read the [alova documentation typescript chapter](/next/tutorial/advanced/in-depth/typescript)
+
+## Limitations
+
+1. [Manage extra states](/next/tutorial/client/in-depth/manage-extra-states) is not supported yet.
diff --git a/docs/resource/03-framework/08-qwik.md b/docs/resource/03-framework/08-qwik.md
new file mode 100644
index 000000000..00c0f06e3
--- /dev/null
+++ b/docs/resource/03-framework/08-qwik.md
@@ -0,0 +1,5 @@
+---
+title: qwik
+---
+
+Comming soon...
diff --git a/docs/resource/03-framework/09-native-mp.md b/docs/resource/03-framework/09-native-mp.md
new file mode 100644
index 000000000..6d5e8bc1c
--- /dev/null
+++ b/docs/resource/03-framework/09-native-mp.md
@@ -0,0 +1,5 @@
+---
+title: Native mini program(China🇨🇳)
+---
+
+Comming soon...
diff --git a/docs/resource/03-framework/10-lit.md b/docs/resource/03-framework/10-lit.md
new file mode 100644
index 000000000..f3b5a8b92
--- /dev/null
+++ b/docs/resource/03-framework/10-lit.md
@@ -0,0 +1,5 @@
+---
+title: lit
+---
+
+Comming soon...
diff --git a/docs/resource/03-framework/11-stencil.md b/docs/resource/03-framework/11-stencil.md
new file mode 100644
index 000000000..cb9b09671
--- /dev/null
+++ b/docs/resource/03-framework/11-stencil.md
@@ -0,0 +1,5 @@
+---
+title: stencil
+---
+
+Comming soon...
diff --git a/docs/tutorial/09-framework/_category_.json b/docs/resource/03-framework/_category_.json
similarity index 92%
rename from docs/tutorial/09-framework/_category_.json
rename to docs/resource/03-framework/_category_.json
index 6edd9139c..fb1ff2abe 100644
--- a/docs/tutorial/09-framework/_category_.json
+++ b/docs/resource/03-framework/_category_.json
@@ -1,6 +1,6 @@
-{
- "label": "Framework",
- "link": {
- "type": "generated-index"
- }
-}
+{
+ "label": "Framework",
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/tutorial/02-getting-started/01-introduce.md b/docs/tutorial/02-getting-started/01-introduce.md
new file mode 100644
index 000000000..485f1f521
--- /dev/null
+++ b/docs/tutorial/02-getting-started/01-introduce.md
@@ -0,0 +1,130 @@
+---
+title: Introduce
+---
+
+import Link from '@docusaurus/Link';
+import NavCard from '@site/src/components/NavCard';
+import SupportList from '@site/src/components/SupportList';
+
+## What is alova?
+
+Alova is an creative next-generation request tool that takes front-end and back-end collaboration and API consumption as its starting point, simplifies API consumption from 7 steps to only 1, saving you most of the work in requesting and making network requests very simple. Let's see how Alova can help you simplify your work.
+
+![](/img/overview_flow_en.png)
+
+It can simplify API consumption from 7 steps to 1 step. You only need to choose the API to use.
+
+## How to do it?
+
+### Core functions
+
+alova provides basic request capabilities similar to axios. You can use it with any request library such as axios and fetch to get out-of-the-box features such as response caching and request sharing.
+
+### Request strategy
+
+Based on the core functions of alova, it also provides a complete solution for complex request scenarios, which we call **request strategy**. With only one line of code, you can quickly implement various complex request logics, which can not only help you improve development efficiency, but also help you improve the running efficiency of the App and reduce the pressure on the server.
+
+For example, `useRequest` can automatically manage the request status, **`loading/error/data` is responsive data**, which can be directly bound in the view in UI frameworks such as react, vue, and svelte, and these responsive data will be automatically maintained according to the request status.
+
+```javascript
+const { loading, error, data } = useRequest(alova.Get('/api/user'));
+```
+
+Another paging request strategy, **when `page/pageSize` changes, it will automatically trigger requests with different parameters**.
+
+```javascript
+const { loading, error, data, page, pageSize, total } = usePagination((page, size) =>
+ alova.Get('/api/user/list', {
+ params: { page, size }
+ })
+);
+```
+
+alova provides 10+ request strategy modules based on the [RSM](/about/RSM) specification, which are implemented in the form of useHook.
+
+### Alova editor extension
+
+Using the alova extension in vscode can help you automatically generate request codes with complete API document annotations and response types. Whether it is a ts project or a js project, you can get complete interface queries, interface details, and smart prompts for response data types.
+
+This extension also optimizes the API consumption process and gives you a different API usage experience. In the past, you needed to query the API document first and constantly switch between the API document and the editor to write request codes. With the alova plug-in, you no longer need to leave the editor and can directly use the API in the editor while checking.
+
+> For a detailed introduction to the alova plug-in, please refer to [Integrated Editor Extension](/next/tutorial/getting-started/extension-integration).
+
+## Is there any difference?
+
+Unlike other request libraries, alova aims to make requests very simple and maintain more efficient data interaction.
+
+We consider both developers and App users. For developers, alova provides them with the ultimate user experience, and for application users, they can enjoy the smooth experience brought by alova's high-performance data interaction.
+
+In addition, let's take a look at the specific features:
+
+- Similar API design to Axios, which makes the user's learning cost lower;
+- High-performance client and server request strategies make the application smoother;
+- High flexibility, Alova's adapter allows Alova to work with any UI framework in any JS environment, and provides a unified user experience and perfect code migration;
+- 2 cache modes and request sharing mechanisms to improve request performance and reduce server pressure;
+- High aggregation organization of API code, each API's request parameters, cache behavior, response data conversion, etc. will be gathered in the same code block, which is a great advantage for managing a large number of APIs;
+
+:::info comparison
+
+You can also check [Comparison with other request libraries](/about/comparison) to learn more about the differences of Alova.
+
+:::
+
+## Run in any JS environment
+
+Not only that, Alova is very flexible, you can use it with different request tools in any of the following JS environments (the gray part will be gradually supported in the future).
+
+
+
+## Online trial
+
+You can run the project directly in the browser via Codesandbox [online editable examples try alovajs](/category/examples), so it's almost indistinguishable from local development without having to install anything on your machine.
+
+## Join alova community
+
+import ImgDiscord from '@site/static/img/discord.svg';
+import ImgX from '@site/static/img/x.svg';
+import ImgWechat from '@site/static/img/wechat.svg';
+import wechatQrcode from '@site/static/img/wechat_qrcode.jpg';
+
+,
+title: 'Discord',
+desc: 'The community\'s GPT robot will answer your questions',
+link: 'https://discord.gg/S47QGJgkVb',
+target: '__blank'
+},
+{
+Image: ,
+title: 'WeChat',
+desc: 'Communicate in group chat and get responses faster',
+link: wechatQrcode,
+target: '__blank'
+},
+{
+Image: ,
+title: 'X',
+desc: 'Follow us and continue to receive the latest news',
+link: 'https://x.com/alovajs',
+target: '__blank'
+}
+]}>
+
+## Welcome to contribute
+
+Before contributing, please be sure to read the [Contribution Guide](/next/contributing/overview) in detail to ensure your effective contribution.
+
+## Start
+
+Next, we will start with the simplest request, then explain the request strategy, understand how alova simplifies your work, and then go into the advanced guide and the best practices summarized in actual projects.
+
+Let’s start learning to send our first request!
+
+
diff --git a/docs/tutorial/02-getting-started/02-quick-start.md b/docs/tutorial/02-getting-started/02-quick-start.md
index 6cdfadc12..c51a3b249 100644
--- a/docs/tutorial/02-getting-started/02-quick-start.md
+++ b/docs/tutorial/02-getting-started/02-quick-start.md
@@ -1,115 +1,130 @@
----
-title: Quick Start
-sidebar_position: 20
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import EmbedSandpack from "@site/src/components/EmbedSandpack";
-
-import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js';
-import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js';
-
-:::tip Example tip
-
-If you haven’t learned about alova yet, it is recommended that you read [alova overview](/tutorial/getting-started) first.
-
-:::
-
-## Install
-
-
-
-
-```bash
-npm install alova --save
-```
-
-
-
-
-```bash
-yarn add alova
-```
-
-
-
-
-```bash
-pnpm add alova
-```
-
-
-
-
-```bash
-bun add alova
-```
-
-
-
-
-> You can also [use alova through CDN](/tutorial/others/use-in-static)
-
-## Create alova instance
-
-In alova, a request needs to be made through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. It is recommended to use the `GlobalFetch` request adapter here, which is a package based on the `fetch API`.
-
-
-
-
-```javascript
-import { createAlova } from 'alova';
-import GlobalFetch from 'alova/GlobalFetch';
-
-const alovaInstance = createAlova({
- requestAdapter: GlobalFetch()
-});
-```
-
-
-
-
-```javascript
-const { createAlova } = require('alova');
-const GlobalFetch = require('alova/GlobalFetch');
-
-const alova = createAlova({
- requestAdapter: GlobalFetch();
-});
-```
-
-> When using GlobalFetch in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/tutorial/request-adapter/alova-adapter-axios/).
-
-
-
-
-```javascript
-import { createAlova } from 'npm:alova';
-import GlobalFetch from 'npm:alova/GlobalFetch';
-
-const alova = createAlova({
- requestAdapter: GlobalFetch();
-});
-```
-
-
-
-
-## GET request
-
-Sending a request via `alovaInstance.Get` will receive a `Response` instance thanks to the `GlobalFetch` request adapter, which is simple.
-
-
-
-In an asynchronous function, you can also use `await alovaInstance.Get` to wait for a response.
-
-## POST request
-
-Submitting data via `alovaInstance.Post` is also easy.
-
-
-
-## What’s next?
-
-In fact, this is just the simplest request example. We will learn more about the features in the next chapters, so let's start learning.
+---
+title: Quick Start
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import EmbedSandpack from "@site/src/components/EmbedSandpack";
+
+import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js';
+import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js';
+
+:::warning beta reminder
+
+alova@3.0 is currently in the beta stage, and some functions may be changed. If you find a bug, please let us know in [Github issues](https://github.com/alovajs/alova/issues/new/choose), and we will solve it as soon as possible.
+
+:::
+
+:::tip Example Tips
+
+If you haven't learned about alova yet, it is recommended that you read [introduce alova](/next/tutorial/getting-started/introduce) first.
+
+:::
+
+## Installation
+
+
+
+
+```bash
+npm install alova@beta --save
+```
+
+
+
+
+```bash
+yarn add alova@beta
+```
+
+
+
+
+```bash
+pnpm add alova@beta
+```
+
+
+
+
+```bash
+bun add alova@beta
+```
+
+
+
+
+## Create an alova instance
+
+In alova, you need to initiate a request through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. Here we recommend using the `alova/fetch` request adapter, which is a wrapper based on the `fetch API` and is very concise.
+
+
+
+
+```javascript
+import { createAlova } from 'alova';
+import fetchAdapter from 'alova/fetch';
+
+const alovaInstance = createAlova({
+ requestAdapter: fetchAdapter()
+});
+```
+
+
+
+
+```javascript
+const { createAlova } = require('alova');
+const fetchAdapter = require('alova/fetch');
+
+const alova = createAlova({
+requestAdapter: fetchAdapter();
+});
+```
+
+> When using fetchAdapter in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/next/resource/request-adapter/alova-adapter-axios/).
+
+
+
+
+```javascript
+import { createAlova } from 'npm:alova';
+import fetchAdapter from 'npm:alova/fetch';
+
+const alova = createAlova({
+requestAdapter: fetchAdapter();
+});
+```
+
+
+
+
+## GET request
+
+Send a request through `alovaInstance.Get`. Since the `fetchAdapter` request adapter is used, a `Response` instance will be received. This is very simple.
+
+```js
+const response = await alovaInstance
+ .Get('https://alovajs.dev/user/profile')
+ .then(response => response.json());
+```
+
+In an asynchronous function, you can also use `await alovaInstance.Get` to wait for the response.
+
+## POST request
+
+Submit data through `alovaInstance.Post`. This is also very simple.
+
+```js
+const response = alovaInstance
+ .Post('https://alovajs.dev/posts', {
+ title: 'foo',
+ body: 'bar',
+ userId: 1
+ })
+ .then(response => response.json());
+```
+
+## What to do next?
+
+In fact, this is just a simple request example. You will learn more functions in the next chapter. Let's start learning.
diff --git a/docs/tutorial/02-getting-started/03-basic/03-method.md b/docs/tutorial/02-getting-started/03-basic/03-method.md
new file mode 100644
index 000000000..543bfb09d
--- /dev/null
+++ b/docs/tutorial/02-getting-started/03-basic/03-method.md
@@ -0,0 +1,317 @@
+---
+title: Method Instance
+---
+
+In the previous chapter, we tried to send a request and get the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can send a request through the `then, catch, finally` method or `await`, just like a Promise object.
+
+```javascript
+const userMethodInstance = alovaInstance.Get('/api/user');
+
+userMethodInstance.then(response => {
+ // ...
+});
+
+userMethodInstance.catch(error => {
+ // ...
+});
+
+userMethodInstance.finally(() => {
+ // ...
+});
+
+try {
+ await userMethodInstance;
+} catch (error) {
+ // ...
+} finally {
+ // ...
+}
+```
+
+Simple way to write:
+
+```javascript
+const response = await alovaInstance.Get('/api/user');
+```
+
+Each method instance describes the type of each request, request url, request header, request parameters, etc. In addition, you can also define request behavior on the method instance to control how the method handles the request.
+
+## Request type
+
+alova provides 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH.
+
+| Instance creation function | Parameters |
+| -------------------------- | --------------------------------------------- |
+| GET | `alovaInstance.Get(url[, config])` |
+| POST | `alovaInstance.Post(url[, data[, config]])` |
+| PUT | `alovaInstance.Put(url[, data[, config]])` |
+| DELETE | `alovaInstance.Delete(url[, data[, config]])` |
+| HEAD | `alovaInstance.Head(url[, config])` |
+| OPTIONS | `alovaInstance.Options(url[, config])` |
+| PATCH | `alovaInstance.Patch(url[, data[, config]])` |
+
+Parameter description:
+
+- `url` is the request path;
+- `data` is the request body data;
+- `config` is the request configuration object, which includes the request header, params parameters, request behavior parameters and other configurations;
+
+You can also create your own custom method instance, which is useful when dynamically specifying the request type.
+
+```javascript
+import { Method } from 'alova';
+
+const method = new Method('GET', alovaInstance, '/api/users', {
+ params: {
+ ID: 1
+ }
+});
+```
+
+Next, let's take a look at how to define request parameters, which should be familiar to you.
+
+## Request parameters
+
+### URL parameters
+
+Use params to pass in URL parameters, and the params parameters will be concatenated after the url in the form of ?.
+
+```javascript
+alovaInstance.Get('/todo/list', {
+ params: {
+ userId: 1
+ }
+});
+```
+
+Of course, you can also concatenate directly after the url, and the effect is the same.
+
+```javascript
+alovaInstance.Get('/todo/list?userId=1');
+```
+
+### Request body
+
+When sending **POST, PUT, DELETE, PATCH requests**, data can be sent through the request body. At this time, the second parameter passed in is the request body. It is worth noting that the POST request can also pass in the params parameter.
+
+```javascript
+alovaInstance.Post(
+ '/todo',
+ // The second parameter is the request body
+ {
+ title: 'test todo',
+ time: '12:00'
+ },
+ // The third parameter is the configuration
+ {
+ params: {
+ userId: 1
+ }
+ }
+);
+```
+
+### Request header
+
+Specify the request header through headers.
+
+```javascript
+alovaInstance.Get('/user', {
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ }
+});
+```
+
+### Parameters supported by other request adapters
+
+In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `alova/fetch` as the request adapter of alova, you can configure any parameters supported by `fetch API` on the method instance, and these parameters will be passed to the `fetch` function when requesting.
+
+```javascript
+alovaInstance.Get('/todo/list', {
+ // ...
+ // highlight-start
+ credentials: 'same-origin',
+ referrerPolicy: 'no-referrer',
+ mode: 'cors'
+ // highlight-end
+});
+```
+
+When the above method instance sends a request through `fetch`, it will request with the following parameters.
+
+```javascript
+fetch('/todo/list', {
+ // ...
+ // highlight-start
+ credentials: 'same-origin',
+ referrerPolicy: 'no-referrer',
+ mode: 'cors'
+ // highlight-end
+});
+```
+
+> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter, such as `alova/fetch` supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters.
+
+If you use other request adapters, you can also pass the parameters they support.
+
+## Request behavior
+
+In [RSM](/about/RSM), request behavior is used to describe how the request will be processed.
+
+### Timeout
+
+Set the request timeout.
+
+```javascript
+// Request timeout at request level
+alovaInstance.Get('/todo/list', {
+ // ...
+ // highlight-start
+ timeout: 10000
+ // highlight-end
+});
+```
+
+### Request sharing
+
+We always encounter this situation. When a request is sent but not responded, the same request is sent again, which causes request waste or repeated submission problems, such as the following three scenarios:
+
+1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be sent at the same time;
+
+2. The submit button is not disabled, and the user clicks the submit button multiple times;
+
+3. When the preload page is entered before the preload is completed, multiple identical requests will be sent;
+
+4. Prevent repeated requests in react's StrictMode;
+
+Shared requests are used to solve these problems. It can not only improve the fluency of the application, but also reduce the pressure on the server.
+
+```mermaid
+flowchart LR
+classDef response fill:#a8bcff
+R1[request 1] --> S1[send request] --> W1[wait for response]:::response --> RE1[receive data 1]
+R2[same request as request 1] --> W1[wait for response]:::response --> RE2[receive data 1]
+```
+
+Request sharing is enabled by default. If you want to turn off sharing requests on a specific request, you can do this:
+
+```javascript
+alovaInst.Get('/todo', {
+ // ...
+ // highlight-start
+ shareRequest: false
+ // highlight-end
+});
+```
+
+:::warning How to identify the same request
+
+The request method, request url, request header, url parameter, and request body of the method instance are used as unique identifiers. Identical identifiers indicate the same request, rather than comparing the reference address of the method instance.
+
+:::
+
+### Transform response data
+
+Sometimes we need to uniformly transform response data. We can set the `transform` function for the method instance to transform the response data into the required structure.
+
+```javascript
+alovaInstance.Get('/todo/list', {
+ // The function accepts the response data and response header data, and requires the transformed data to be returned.
+ transform(rawData, headers) {
+ return rawData.list.map(item => {
+ return {
+ ...item,
+ statusText: item.done ? 'Completed' : 'In progress'
+ };
+ });
+ }
+});
+```
+
+### Response cache
+
+Response cache allows you to better utilize server-side data multiple times without sending a request to obtain data every time. The GET request will set a memory cache time of 5 minutes by default. If you don't need it, you can turn off the cache for the current request in the following way.
+
+```ts
+alovaInstance.Get('/todo/list', {
+ // Set to 0 or null to turn off the default response cache
+ cacheFor: 0
+});
+```
+
+For details, please refer to [Response Cache](/next/tutorial/cache/mode)
+
+## Interrupt request
+
+Call the `abort` of the method instance to interrupt the request.
+
+```javascript
+const userMethod = alovaInstance.Get('/api/user');
+userMethod.then(res => {
+ // ...
+});
+
+const handleCancel = () => {
+ // highlight-start
+ userMethod.abort();
+ // highlight-end
+};
+```
+
+## Listen for upload and download progress
+
+By binding the upload progress event through the `onUpload` of the method instance, and the download progress event through the `onDownload`, it will return the unbinding function.
+
+```javascript
+const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData);
+const offUploadEvent = uploadMethod.onUpload(event => {
+ console.log('File size:', event.total);
+ console.log('Uploaded:', event.loaded);
+});
+
+uploadMethod.then(res => {
+ // ...
+});
+
+// Unbind upload callback
+const handleOffEvent = () => {
+ offUploadEvent();
+};
+```
+
+```javascript
+const downloadMethod = alovaInstance.Get('/todo/downloadfile');
+const offDownloadEvent = downloadMethod.onDownload(event => {
+ console.log('File size:', event.total);
+ console.log('Downloaded:', event.loaded);
+});
+
+downloadMethod.then(res => {
+ // ...
+});
+
+// Unbind download callback
+const handleOffEvent = () => {
+ offDownloadEvent();
+};
+```
+
+:::warning Use `alova/fetch` adapter to pay attention to
+
+Due to the limitation of fetch api, the one provided by alova `alova/fetch` adapter does not support upload progress. If you need upload progress, please use [XMLHttpRequest adapter](/next/resource/request-adapter/alova-adapter-xhr) or [axios adapter](/next/resource/request-adapter/alova-adapter-axios).
+
+You can also write your own request adapter, see [Writing a request adapter](/next/tutorial/advanced/custom/http-adapter) for details.
+
+:::
+
+**Upload/download progress type**
+
+```typescript
+type Progress = {
+ /** Total amount of data uploaded or downloaded */
+ total: number;
+ /** Completed data */
+ loaded: number;
+};
+```
diff --git a/docs/tutorial/02-getting-started/03-basic/04-alova.md b/docs/tutorial/02-getting-started/03-basic/04-alova.md
new file mode 100644
index 000000000..93259774b
--- /dev/null
+++ b/docs/tutorial/02-getting-started/03-basic/04-alova.md
@@ -0,0 +1,78 @@
+---
+title: Alova Instance
+---
+
+Alova instances can not only create method instances of different types, but also set global parameters. The created method instances will inherit the parameters of this alova instance. When the parameters of the alova instance are set to the same parameters as the method instance, such as `timeout`, `shareRequest`, etc., the parameters of the method instance will be used first.
+
+Next we look at the global parameters of alova.
+
+## baseURL
+
+After setting the baseURL, you no longer need to add the same url prefix for every request.
+
+```javascript
+const alovaInstance = createAlova({
+ baseURL: 'https://api.alovajs.dev'
+ // ...
+});
+```
+
+At this point, you only need to specify pathname when creating a method instance.
+
+```javascript
+alovaInstance.Get('/todo/list');
+```
+
+## Global timeout
+
+The following is to set the global request timeout.
+
+```javascript
+// Set request timeout globally
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ // Request timeout, unit is milliseconds, default is 0, which means it will never timeout
+ timeout: 50000
+ // highlight-end
+});
+```
+
+## Global sharing request
+
+Set sharing requests globally.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ shareRequest: false
+ // highlight-end
+});
+```
+
+## Request adapter
+
+In the previous chapter we have configured the `alova/fetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments.
+
+- [Mock request adapter](/next/resource/request-adapter/alova-mock)
+- [XMLHttpRequest Adapter](/next/resource/request-adapter/alova-adapter-xhr)
+- [axios adapter](/next/resource/request-adapter/alova-adapter-axios)
+- [uniapp adapter](/next/resource/request-adapter/alova-adapter-uniapp)
+- [taro adapter](/next/resource/request-adapter/alova-adapter-taro)
+
+## Global response cache
+
+You can also set the response cache globally:
+
+```ts
+const alovaInstance = createAlova({
+ // ...
+ cacheFor: {
+ GET: 0, // Close all GET caches
+ POST: 60 * 60 * 1000 // Set all POST caches for 1 hour
+ }
+});
+```
+
+For more details, please refer to [Response Cache](/next/tutorial/cache/mode)
diff --git a/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md b/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md
new file mode 100644
index 000000000..ebf1ccf54
--- /dev/null
+++ b/docs/tutorial/02-getting-started/03-basic/05-global-interceptor.md
@@ -0,0 +1,135 @@
+---
+title: Global Interceptor
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Global request interceptor
+
+Usually, we need to use the same configuration for all requests, such as adding token and timestamp to the request header. At this time, we can set a global request interceptor, which will be triggered before all requests. We can set this interceptor Set request parameters uniformly.
+
+```mermaid
+flowchart LR
+ R1[Request 1] --> beforeRequest
+ R2[Request 2] --> beforeRequest
+ R3[Request 3] --> beforeRequest
+ RN[Request N] --> beforeRequest
+ beforeRequest --> S1[Send request]
+```
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ //The function parameter is a method instance, including request data such as url, params, data, headers, etc.
+ // You can modify these data freely
+ // highlight-start
+ beforeRequest(method) {
+ // Suppose we need to add token to the request header
+ method.config.headers.token = 'token';
+ }
+ // highlight-end
+});
+```
+
+You can also set beforeRequest as an asynchronous function.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ async beforeRequest(method) {
+ //Perform some asynchronous tasks
+ // ...
+ }
+ // highlight-end
+});
+```
+
+## Global response interceptor
+
+When we want to uniformly parse response data, uniformly handle errors, and uniformly handle request completion, we can specify a global response interceptor when creating an alova instance. The response interceptor includes an interceptor for successful requests and an interceptor for failed requests. and request completion interceptors.
+
+```mermaid
+flowchart LR
+ classDef error fill:#f96,stroke:#f00,stroke-width:2px;
+
+ R1[Request 1 successful] --> responded.onSuccess
+ R2[Request 2 successful] --> responded.onSuccess
+ RN[Request N successful] --> responded.onSuccess
+ R4[Request 4 failed]:::error --> responded.onError:::error
+ R5[Request M failed]:::error --> responded.onError:::error
+ responded.onSuccess --> responded.onComplete
+ responded.onError --> responded.onComplete
+```
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ //Use two items of the array to specify the interceptor for successful request and the interceptor for failed request respectively.
+ responded: {
+ // highlight-start
+ // Interceptor for successful request
+ // When using the alova/fetch request adapter, the first parameter receives the Response object
+ // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request.
+ onSuccess: async (response, method) => {
+ if (response.status >= 400) {
+ throw new Error(response.statusText);
+ }
+ const json = await response.json();
+ if (json.code !== 200) {
+ // This request will throw an error when an error is thrown or a Promise instance with reject status is returned.
+ throw new Error(json.message);
+ }
+
+ //The parsed response data will be passed to the transform hook function of the method instance. These functions will be explained later.
+ return json.data;
+ },
+ // highlight-end
+
+ // highlight-start
+ // Interceptor for request failure
+ // This interceptor will be entered when a request error occurs.
+ // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request.
+ onError: (err, method) => {
+ alert(error.message);
+ },
+ // highlight-end
+
+ // highlight-start
+ //Interceptor for request completion
+ // When you need to execute logic whether the request succeeds, fails, or hits the cache, you can specify a global `onComplete` interceptor when creating an alova instance, such as turning off the request loading state.
+ // Receive the method instance of the current request
+ onComplete: async method => {
+ // Process request completion logic
+ }
+ // highlight-end
+ }
+});
+```
+
+If you do not need to set an interceptor for request failure or completion, you can directly pass in the interceptor function for successful request, instead of setting a callback through an object.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ async responded(response, method) {
+ // Interceptor for successful request
+ }
+ // highlight-end
+});
+```
+
+:::info Interceptor triggering instructions
+
+When you use `alova/fetch` to request the adapter, due to the characteristics of `window.fetch`, the `onError` interceptor will only be triggered when the connection times out or the connection is aborted. In other cases, the `onSuccess` interceptor will be triggered. [For details, please Check here](https://developer.mozilla.org/docs/Web/API/fetch)
+
+:::
+
+:::warning Special attention
+
+1. `onSuccess`, `onError` and `onComplete` can be set as synchronous functions and asynchronous functions.
+2. The `onError` callback is a capture function for request errors. An error thrown in `onSuccess` will not trigger `onError`. When an error is caught but no error is thrown or a Promise instance that returns reject status is used, the request will be considered successful and no response data will be obtained.
+
+:::
diff --git a/docs/tutorial/02-getting-started/03-basic/06-method-metadata.md b/docs/tutorial/02-getting-started/03-basic/06-method-metadata.md
new file mode 100644
index 000000000..75f14ca0e
--- /dev/null
+++ b/docs/tutorial/02-getting-started/03-basic/06-method-metadata.md
@@ -0,0 +1,150 @@
+---
+title: Method Metadata
+---
+
+Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata.
+
+## Use metadata to identify identities
+
+### Use identity before request
+
+For example, most of the interfaces in your project need to be accompanied by `token` for each request, but there are still some interfaces that do not require verification. You may handle them uniformly in the global `beforeRequest` function.
+
+```javascript
+const nonvalidateRequiredApi = [
+ '/api/url1',
+ '/api/url2',
+ '/api/url3'
+ // ...
+];
+
+createAlova({
+ beforeRequest(method) {
+ if (!nonvalidateRequiredApi.includes(method.url)) {
+ method.config.headers.token = '...';
+ }
+ }
+});
+```
+
+This will cause the following two problems:
+
+1. Information is not aggregated with method instances, making maintainability even worse;
+2. Coding is more troublesome;
+
+To solve these two problems, we will use metadata to identify a specific method instance when it is created.
+
+**Step 1: Define metadata when creating method instance**
+
+```javascript
+const loginAPI = (username, password) => {
+ const methodInstance = alovaInst.Post('/login', {
+ username,
+ password
+ });
+ methodInstance.meta = {
+ ignoreToken: true
+ };
+ return methodInstance;
+};
+```
+
+And you can also directly define metadata in config
+
+```javascript
+const loginAPI = (username, password) => {
+ return alovaInst.Post(
+ '/login',
+ {
+ username,
+ password
+ },
+ {
+ meta: {
+ ignoreToken: true
+ }
+ }
+ );
+};
+```
+
+**Step 2: Use metadata as the basis for judgment in `beforeRequest`**
+
+```javascript
+createAlova({
+ // ...
+ beforeRequest(method) {
+ if (!method.meta?.ignoreToken) {
+ method.config.headers.token = '...';
+ }
+ }
+});
+```
+
+### Use the identity after the response
+
+This method can also be used in global `responded`. For example, in most cases, the request api will return json data, but there may be a file download interface, which will return a binary data stream. In this case Below, you can use different metadata in `responded` to handle different responses separately.
+
+**Step one: When creating a method instance, you also need to assign a metadata**
+
+```javascript
+const downloadAPI = filePath => {
+ const methodInstance = alovaInst.Post('/download_file', {
+ filePath
+ });
+ methodInstance.meta = {
+ isDownload: true
+ };
+ return methodInstance;
+};
+```
+
+**Step 2: Use metadata in `responded` as a basis for judgment**
+
+```javascript
+createAlova({
+ // ...
+ responded:
+ onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json()
+ onError: (error, method) => {
+ //Metadata of method instances can also be accessed when responding to errors
+ }
+ }
+});
+```
+
+## Use metadata to pass information
+
+In some cases, if you want to add additional information to different method instances for use elsewhere, you can also use metadata to save it. Take uniformly generating different method instance IDs as an example.
+
+```javascript
+createAlova({
+ beforeRequest(method) {
+ if (!method.meta.generateId) {
+ method.meta.uid = generateUUID();
+ }
+ },
+
+ responded: {
+ onSuccess(response, method) {
+ // Access the meta data generated by the current method when the request is successful.
+ const currentMethodUID = method.meta.uid;
+ },
+ onError(error, method) {
+ //Access the meta data generated by the current method when the request fails.
+ const currentMethodUID = method.meta.uid;
+ }
+ }
+});
+```
+
+## Tips for non-typescript projects
+
+In a non-typescript environment, you can use any attribute as an information carrier, not limited to the `meta` attribute.
+
+```javascript
+methodInstance.showResponseMsg = true;
+methodInstance.others = 'abc';
+```
+
+Only in the typescript environment, any attribute name will report that the attribute "$0" does not exist. ts(2339)`, so in the type we specify the `meta` attribute as the information carrier.
diff --git a/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md b/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md
new file mode 100644
index 000000000..d0ca6d2bf
--- /dev/null
+++ b/docs/tutorial/02-getting-started/03-basic/07-combine-framework.md
@@ -0,0 +1,298 @@
+---
+title: Combine UI framework
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import CodeBlock from '@theme/CodeBlock';
+import EmbedSandpack from "@site/src/components/EmbedSandpack";
+
+import useRequestVue from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/vueComposition-useRequest.en.vue';
+import useRequestReact from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/react-useRequest.en.jsx';
+import useRequestSvelte from '!!raw-loader!@site/codesandbox@3/01-getting-started/07-combine-framework/svelte-useRequest.en.svelte';
+
+Next, we will learn how to use it in conjunction with the client UI framework, which can allow alova to exert its true power. When used in the UI framework, not only can alova automatically manage the responsive request status, but also automatically control when the request should be sent through certain rules.
+
+`alova` provides 10+ client request strategies, which help you implement complex requests in a simple and elegant way. Let's continue to look down!
+
+## Set statesHook
+
+Before using the request strategy, we need to set the corresponding statesHook on the alova instance. It must correspond to the UI framework used by the project. This is very important. It will tell alova that it should create responsive states for the corresponding UI framework. Currently, the following frameworks are supported:
+
+
+
+
+
+```js
+import { createAlova } from 'alova';
+import VueHook from 'alova/vue';
+
+export const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ statesHook: VueHook
+ // highlight-end
+});
+```
+
+
+
+
+
+```js
+import { createAlova } from 'alova';
+import ReactHook from 'alova/react';
+
+export const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ statesHook: ReactHook
+ // highlight-end
+});
+```
+
+
+
+
+```js
+import { createAlova } from 'alova';
+import SvelteHook from 'alova/svelte';
+
+export const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ statesHook: SvelteHook
+ // highlight-end
+});
+```
+
+
+
+
+## Automatically manage request status
+
+`useRequest` is our most commonly used request strategy. It can help us create and maintain responsive states of requests, such as `loading/data/error`, etc. You can use these responsive states directly in the view. When they change, the view will also change accordingly.
+
+useRequest means sending a request. By default, a request will be sent when called.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{useRequestSvelte}
+
+
+
+
+[When to use useRequest and when to send a request via `await alovaInstance.Get`](/next/tutorial/project/best-practice/skills).
+
+:::warning useHook usage specification
+
+Please note that `useRequest` can only be used to send requests within a component. Outside a component, you can send requests directly through a method instance, and the use of `useRequest` must comply with the use hook usage rules, that is, it can only be called at the outermost level of a function.
+
+**❌❌❌ It is not recommended to call it in a loop, conditional judgment, or sub-function**. For example, the following example of use in a click callback, when used in a callback function, although the request can be initiated normally, the responsive data returned by the use hook cannot be used in the view, and the same is true for loops and conditional judgments.
+
+```javascript
+// ❌ bad
+const handleClick = () => {
+ const { loading, data } = useRequest(getter);
+};
+
+// -------
+// ✅ good
+const { loading, data, send } = useRequest(getter, {
+ immediate: false
+});
+const handleClick = () => {
+ send();
+};
+```
+
+:::
+
+## Submit data
+
+When you need to submit a new todo item, you can first turn off the default send request, switch to manual trigger request, and receive the `send` function in useRequest for manual request sending. The `send` function will return a Promise instance with response data, which will be changed to resolve state after the request response.
+
+At this time, in order to receive the parameters passed in by the `send` function, you can set the first parameter of `useRequest` to a function, which we call **method handler**.
+
+```javascript
+const {
+ // ...
+ // Function for manual sender request, send request after calling
+ send: addTodo
+
+ // Parameters of send function will be received here
+} = useRequest(newTodo => alovaInstance.Post('/todo', newTodo), {
+ // When immediate is false, it is not sent by default
+ immediate: false
+});
+
+// Send request manually
+const handleAddTodo = () => {
+ const newTodo = {
+ title: 'New todo item',
+ time: new Date().toLocaleString()
+ };
+ // The send function returns a Promise object, which can receive response data
+ addTodo(newTodo)
+ .then(result => {
+ console.log('Add todo item successfully, response data is:', result);
+ })
+ .catch(error => {
+ console.log('Add todo item failed, error message is:', error);
+ });
+};
+```
+
+The `send` function allows you to freely repeat requests.
+
+> In react, the send function uses the `useCallback` package, and it is not restricted by the closure trap. You can use it directly in the event without worrying about performance problems.
+
+## Process Response
+
+After the request is completed, the response data will be processed through multiple processes before the final data is obtained at the location where the request was sent. The process is as follows:
+
+```mermaid
+flowchart LR
+ classDef condition fill:#a8bcff
+
+ R1[Response successfully] --> global.onSuccess
+ global.onSuccess --> global.onComplete
+ global.onSuccess --> throw{Is an error thrown? }:::condition
+ throw -->|No| method.transform
+ method.transform --> useHook.onSuccess
+ throw -->|Yes| useHook.onError
+
+ method.transform --> throw2{Is an error thrown? }:::condition
+ throw2 -->|No| useHook.onSuccess
+ throw2 -->|Yes| useHook.onError
+
+ useHook.onSuccess --> throw3{Throw an error? }:::condition
+ throw3 -->|Yes| useHook.onError
+
+ R2[Response Error] --> global.onError
+ global.onError --> global.onComplete
+ global.onError --> throw4{Is an error thrown? }:::condition
+ throw4 -->|Yes| useHook.onError
+ throw4 -->|No| method.transform
+```
+
+When no error is thrown, the next node receives the return value of the previous node.
+
+### Transform response data
+
+In [method detailed explanation](/next/tutorial/getting-started/basic/method), we have already learned about `transform`, which is also very useful when used in useHook. It allows useHook's data to receive the transformed data without transform again.
+
+```javascript
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ // The function accepts raw data and response header objects, and requires the transformed data to be returned, which will be assigned to the data state.
+ // Note: rawData is the data filtered by the global response interceptor (if it is set). For the configuration of the response interceptor, please refer to the [Setting the Global Response Interceptor] chapter.
+ transform(rawData, headers) {
+ return rawData.list.map(item => ({
+ ...item,
+ statusText: item.done ? 'Completed' : 'In progress'
+ });
+ }
+});
+```
+
+```javascript
+const { data } = useRequest(todoListGetter);
+const { data } = useWatcher(() => todoListGetter, [userInfo]);
+```
+
+The data value will receive the transformed data format.
+
+```typescript
+type data = {
+ // ...
+ statusText: 'Completed' | 'In progress';
+}[];
+```
+
+:::warning note
+
+When used in usehooks, throwing an error in `transform` will also trigger `onError`;
+
+:::
+
+### Bind response callback
+
+If you need to set a request callback, you can also receive the callback setting function in the return parameter of useHooks, as follows:
+
+```javascript
+const {
+ // ...
+
+ //Successful callback binding
+ onSuccess,
+
+ // Failure callback binding
+ onError,
+
+ // Complete the callback binding, the callback will be called on success or failure
+ onComplete
+} = useRequest(todoListGetter);
+onSuccess(event => {
+ console.log('The request was successful, the response data is:', event.data);
+ console.log('The method instance of this request is:', event.method);
+ console.log('Whether the response data comes from the cache:', event.fromCache);
+});
+onError(event => {
+ console.log('The request failed, the error message is:', event.error);
+ console.log('The method instance of this request is:', event.method);
+});
+onComplete(event => {
+ // event.status is success when it succeeds and error when it fails.
+ console.log('The request is completed, the status is: ', event.status);
+ console.log('The method instance of this request is:', event.method);
+ console.log('Whether the response data comes from the cache:', event.fromCache);
+ if (event.data) {
+ console.log('Request data:', event.data);
+ } else if (event.error) {
+ console.log('Error message:', event.error);
+ }
+});
+```
+
+We also support the chain call of binding functions in all useHooks。
+
+```js
+const { data, loading, error, onSuccess, onError, onComplete } = useRequest(todoListGetter)
+ .onSuccess(event => {
+ // ...
+ })
+ .onError(event => {
+ // ...
+ })
+ .onComplete(event => {
+ // ...
+ });
+```
+
+:::note Hint
+
+Throwing an error in `onSuccess` will trigger `onError`.
+
+:::
+
+## End
+
+The above is the basic use of our most commonly used `useRequest`. Other commonly used request strategies include:
+
+1. useWatcher: monitor data changes and automatically request
+2. useForm: form data submission and management
+3. useAutoRequest: automatically request according to rules such as timed polling, browser focus, network reconnection, etc.
+4. ...
+
+For complete usage or other client request strategies, please move to [Client Strategy](/next/tutorial/client/strategy) to view all client request strategies provided by alova.
diff --git a/docs/tutorial/02-getting-started/03-basic/08-server.md b/docs/tutorial/02-getting-started/03-basic/08-server.md
new file mode 100644
index 000000000..f38fa6a59
--- /dev/null
+++ b/docs/tutorial/02-getting-started/03-basic/08-server.md
@@ -0,0 +1,149 @@
+---
+title: Server Usage
+---
+
+As mentioned in the previous [Quick Start](/next/tutorial/getting-started/quick-start), alova can be used in server-side environments such as `nodejs/deno/bun`. You can use all functions except client request strategies on the server. In addition, alova also provides good support for server-side environments.
+
+```js
+import { createAlova } from 'alova';
+import fetchAdapter from 'alova/fetch';
+
+const alovaInstance = createAlova({
+requestAdapter: fetchAdapter()
+});
+
+alovaInstance.Get(...);
+alovaInstance.Post(...);
+```
+
+## Server hooks
+
+On the server, we also have different request scenarios, such as request retries, calling APIs to send verification codes, etc., which we call `Server hooks`. They are decorated functions of method instances, and multiple `Server hooks` can be used in combination.
+
+The following is an example that combines `retry` and `sendCaptcha`, which implements sending verification codes by retrying after failure:
+
+```js
+const { retry, sendCaptcha } = require('alova/server');
+
+const email = 'xxx@xxx.com';
+// Create a method instance for sending verification codes
+const captchaMethod = alovaInstance.Post('/api/captcha', {
+ email,
+ content: 'captcha content'
+});
+
+// Use retry hook to wrap captchaMethod
+const retringMethod = retry(captchaMethod, {
+ retry: 3,
+ backoff: {
+ delay: 2000
+ }
+});
+// Use sendCaptcha hook to wrap retringMethod, and send the request and get the response result through await
+const result = await sendCaptcha(retringMethod, {
+ key: email
+});
+```
+
+You can also use multiple `server hooks` to wrap method instances directly.
+
+```ts
+const result = await sendCaptcha(
+ retry(
+ alovaInstance.Post('/api/captcha', {
+ email,
+ content: 'captcha content'
+ }),
+ {
+ retry: 3,
+ backoff: {
+ delay: 2000
+ }
+ }
+ ),
+ {
+ key: email
+ }
+);
+```
+
+> For more `Server hooks`, please visit [Server Strategy](/next/tutorial/server/strategy).
+
+## Multi-level Cache
+
+alova provides a complete and simple cache function. Not only that, it also supports multi-level cache to provide the fastest request experience for your server application. You can freely choose to use single-level cache or multi-level cache. Their operating mechanisms are as follows:
+
+```mermaid
+flowchart LR
+A[User request] --> B{Check L1 cache}
+B -->|Hit| C[Return data]
+B -->|Miss| D{Check L2 cache}
+D -->|Hit| E[Update L1 cache]
+E --> C
+D -->|Miss| F[Request API interface]
+F --> G[Update L2 cache]
+G --> E
+C --> H[End]
+
+style F stroke-width:8px
+```
+
+Some application scenarios are as follows:
+
+1. High access frequency and low latency requirements: such as popular news and product details, which can further reduce network overhead and maintain faster response when the network is unstable.
+2. Reduce the pressure on downstream servers, such as services with peak access periods, and the upper-level cache can effectively reduce the pressure on the backend database and microservices.
+3. Integrate data merging and processing of multiple downstream servers. Multiple serial requests may lead to longer response time and may consume performance due to complex data conversion. The converted data can be cached.
+4. API rate limit and billing. Weather forecast service API updates weather information every hour, geographic location data API, etc.
+
+The following is an example of using an inter-process memory sharing adapter plus lru cache as the first-level cache and redis as the second-level cache:
+
+```js
+const { createPSCAdapter, NodeSyncAdapter } = require('@alova/psc');
+const { LRUCache } = require('lru-cache');
+const { createRedisAdapter } = require('@alova/redis');
+
+function lRUCache(options = {}) {
+ const cache = new LRUCache(options);
+ return {
+ set(key, value) {
+ return cache.set(key, value);
+ },
+
+ get(key) {
+ return cache.get(key);
+ },
+
+ remove(key) {
+ return cache.delete(key);
+ },
+
+ clear() {
+ return cache.clear();
+ }
+ };
+}
+
+const alovaInstance = createAlova({
+ baseURL: 'https://api.alovajs.dev',
+
+ // Inter-process shared cache adapter
+ l1Cache: createPSCAdapter(
+ NodeSyncAdapter(),
+ lRUCache({
+ max: 1000,
+ ttl: 1000 * 60 * 10
+ })
+ ),
+
+ // redis cache adapter
+ l2Cache: createRedisAdapter({
+ host: 'localhost',
+ port: 6379,
+ username: 'default',
+ password: 'my-top-secret',
+ db: 0
+ })
+});
+```
+
+> For a deeper understanding of response caching, please visit [Cache Details](/next/tutorial/cache).
diff --git a/docs/tutorial/02-getting-started/03-basic/_category_.json b/docs/tutorial/02-getting-started/03-basic/_category_.json
new file mode 100644
index 000000000..c000b83bf
--- /dev/null
+++ b/docs/tutorial/02-getting-started/03-basic/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Basic"
+}
diff --git a/docs/tutorial/02-getting-started/09-extension-integration.md b/docs/tutorial/02-getting-started/09-extension-integration.md
new file mode 100644
index 000000000..f0f02351b
--- /dev/null
+++ b/docs/tutorial/02-getting-started/09-extension-integration.md
@@ -0,0 +1,130 @@
+---
+title: Extension integration
+---
+
+Integrating Alova's editor extension can make it more powerful.
+
+1. Automatically generate request code and response data types, and experience smart prompts for interface data in js projects.
+2. Embed api documents in the code to experience the effect of checking and using APIs.
+3. Update api regularly and actively notify front-end development, no longer relying on server-side developers to notify.
+
+
+
+:::info
+
+The extension will be released soon...
+
+:::
+
+> Automatically generate support for swagger-v2 and openapi-v3 specifications.
+
+## Configuration
+
+When using the extension, you need to specify the input source and output directory from the openapi file, etc. Create `alova.config.js` in the project root directory. The specific configuration is as follows:
+
+```js
+// alova.config.js
+module.exports = {
+ // API generation setting array, each item represents an automatically generated rule, including the generated input and output directories, standard file addresses, etc.
+ generator: [
+ // Server 1
+ {
+ // Input parameter 1: openapi json file url address
+ input: 'http://localhost:3000/openapi.json',
+
+ // Input parameter 2: local address with the current project as the relative directory
+ // input: 'openapi/api.json'
+
+ // Input parameter 3: When there is no direct reference to the openapi file, it is a document address, and the document type must be specified with the platform parameter
+ // input: 'http://192.168.5.123:8080'
+
+ // (Optional) platform is a platform that supports openapi. Currently only swagger is supported. The default is empty
+ // When this parameter is specified, the input field only needs to specify the address of the document without specifying the openapi file
+ platform: 'swagger',
+
+ // Output path of interface file and type file. Multiple generators cannot have the same address, otherwise the generated code will overwrite each other
+ output: 'src/api',
+
+ // (Optional) Specify the mediaType of the generated response data. Use this data type to generate the response ts format of the 200 status code. The default is application/json
+ responseMediaType: 'application/json',
+
+ // (Optional) Specify the bodyMediaType of the generated request body data. Use this data type to generate the ts format of the request body. The default is application/json
+ bodyMediaType: 'application/json',
+
+ /**
+ * (Optional) The type of generated code. The optional values are auto/ts/typescript/module/commonjs. The default is auto. The type of the current project will be determined by certain rules. If the generation is incorrect, you can also customize the specified type:
+ * ts/typescript: The same meaning, indicating the generation of ts type files
+ * module: Generate esModule specification files
+ * commonjs: Indicates the generation of commonjs specification files
+ */
+ type: 'auto',
+
+ /**
+ * (Optional) Filter or convert the generated api interface function, return a new apiDescriptor to generate the api call function, if this function is not specified, the apiDescripor object is not converted
+ */
+ handleApi: apiDescriptor => {
+ // Returning a falsy value means filtering this api
+ if (!apiDescriptor.path.startWith('/user')) {
+ return;
+ }
+
+ apiDescriptor.parameter = apiDescriptor.parameter.filter(
+ param => param.in === 'header' && param.name === 'token'
+ );
+ delete apiDescriptor.requestBody.id;
+ apiDescriptor.url = apiDescriptor.url.replace('/user', '');
+ return apiDescriptor;
+ }
+ },
+
+ // Server 2
+ {
+ // ...
+ }
+ ],
+
+ // (Optional) Whether to automatically update the interface, enabled by default, checked every 5 minutes, disabled when false
+ autoUpdate: true
+
+ /* You can also configure more detailed parameters
+ autoUpdate: {
+ // Update when the editor is opened, false by default
+ launchEditor: true,
+ // Automatic update interval, in milliseconds
+ interval: 5 * 60 * 1000
+ }
+ */
+};
+```
+
+## Usage
+
+The generated API code is accessed through the global `Apis` by default. You can enjoy the smart prompts provided by the editor to quickly preview the API information, allowing you to use the API while checking.
+
+API parameters will be specified through parameters such as `params/pathParams/data/headers`, and you can also specify the config parameters of the method instance.
+
+```js
+Apis.user.changeProfile({
+ // (optional)query parameters
+ params: {
+ id: 12
+ },
+ // (optional)path parameters
+ pathParams: {
+ id2: 20
+ },
+ // (optional)body parameters
+ data: {
+ name: 'alova',
+ age: 18
+ },
+ // (optional)header parameters
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+
+ // config configuration items supported by other methods
+ cacheFor: 100 * 1000,
+ transform: response => response.detail
+});
+```
diff --git a/docs/tutorial/02-getting-started/10-congratulations.md b/docs/tutorial/02-getting-started/10-congratulations.md
new file mode 100644
index 000000000..7f92b6c90
--- /dev/null
+++ b/docs/tutorial/02-getting-started/10-congratulations.md
@@ -0,0 +1,25 @@
+---
+title: Conclusion
+---
+
+import NavCard from '@site/src/components/NavCard';
+
+🎉 Congratulations! You have completed the basic use of alova. So far, you can use alova to meet your daily project development. You can freely choose the next learning content according to your own interests.
+
+
diff --git a/docs/tutorial/02-getting-started/_category_.json b/docs/tutorial/02-getting-started/_category_.json
index 6a7cbc87e..ac034c06d 100644
--- a/docs/tutorial/02-getting-started/_category_.json
+++ b/docs/tutorial/02-getting-started/_category_.json
@@ -1,3 +1,5 @@
-{
- "label": "Getting Started"
-}
+{
+ "label": "Getting Started",
+ "collapsed": false,
+ "collapsible": false
+}
diff --git a/docs/tutorial/03-client/01-strategy/01-use-request.md b/docs/tutorial/03-client/01-strategy/01-use-request.md
new file mode 100644
index 000000000..133fd6778
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/01-use-request.md
@@ -0,0 +1,225 @@
+---
+title: Auto Manage States
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info Strategy type
+
+use hook
+
+:::
+
+useRequest indicates the sending of a request. By default, a request will be sent when it is called. In enterprise-level projects, it is very important to display the data transmission status in the view. When the page obtains initial data or submits data, useRequest is one of the most commonly used use hooks.
+
+## Usage
+
+Its basic usage has been introduced in detail in [Basic - Combining UI Framework](/next/tutorial/getting-started/basic/combine-framework).
+
+### Set initial data
+
+`data` defaults to `undefined` before the request is successful, but sometimes we need data to have an initial value before the request is successful. For example, when requesting a list, it usually needs to be initialized to `[]`, otherwise it will cause an error when rendering the view because it cannot be looped.
+
+```javascript
+const { data } = useRequest(todoListGetter, {
+ // highlight-start
+ // Initial value of data before request response
+ initialData: []
+ // highlight-end
+});
+```
+
+You can also set `initialData` to a function to dynamically set the initial value. For example, if you don't want the Loading icon to be displayed every time the app is entered and want to use old data instead, you can return the old data as the initial value, which provides a better experience than Loading.
+
+```js
+const { data, loading, error } = useRequest(todoListGetter, {
+ initialData() {
+ // Set the last response data
+ const storedData = localStorage.getItem('placeholder-data');
+ return JSON.parse(storedData || '{}');
+
+ // Also use alova's level2 storage adapter
+ // return alovaInst.l2cache.get('placeholder-data');
+ }
+}).onSuccess(({ data, method }) => {
+ // Save response data
+ localStorage.setItem('placeholder-data', JSON.stringify(data));
+
+ // Also use alova's level2 storage adapter
+ // alovaInst.l2cache.set('placeholder-data', data);
+});
+```
+
+### Do not send request immediately
+
+Set `immediate` to `false` to avoid immediate request.
+
+```javascript
+const { data } = useRequest(todoListGetter, {
+ // highlight-start
+ immediate: false
+ // highlight-end
+});
+```
+
+### Force request
+
+Force request refers to a mechanism that bypasses cache check to trigger request sending, which is useful when you need to get the latest data under certain conditions.
+
+```javascript
+useRequest(todoListGetter, {
+ // highlight-start
+ force: true
+ // highlight-end
+});
+```
+
+It can also be set to a function. When the function return value is `true`, the request is forced.
+
+```javascript
+useRequest(todoListGetter, {
+ // highlight-start
+ force: ({ method, sendArgs }) => {
+ return !!sendArgs[0];
+ }
+ // highlight-end
+});
+```
+
+### Send request manually
+
+Set the first parameter of `useRequest` to a function.
+
+```javascript
+const {
+ // ...
+ // Function for manual sender request, call to send request
+ // Parameters of send function will be received here
+} = useRequest(newTodo => alovaInstance.Post('/todo', newTodo), {
+ // When immediate is false, it is not sent by default
+ immediate: false
+});
+
+send({
+ /* todo data */
+});
+```
+
+The `send` function allows you to freely repeat the request.
+
+> In react, the send function is wrapped with `useCallback`, and it is not restricted by closure traps. You can use it directly in events without worrying about performance issues.
+
+In `useRequest` and `useWatcher`, we can call the `send` function to manually trigger requests. When the send function triggers a request, you can pass in any number of parameters, which can actually be received in the following 3 locations.
+
+Calling `send` supports passing in any number of parameters, which can be received in the following 3 locations.
+
+#### Received in method handler
+
+When their first parameter is set to a callback function, it can be received, which is usually useful when deleting list items, as follows:
+
+```javascript
+const { send } = useRequest(id =>
+ // id will receive 1
+ removeTodoPoster(id)
+);
+send(1);
+```
+
+#### Received in event callback function
+
+Received in the event callback function through `event.sendArgs`, which is an array containing all the parameters of the send function.
+
+```javascript
+const { send, onSuccess, onError, onComplete } = useRequest(newTodo =>
+ alovaInstance.Post('/todo', newTodo)
+);
+onSuccess(event => {
+ // sendArgs value is [1]
+ console.log(event.sendArgs);
+});
+onError(event => {
+ // sendArgs value is [1]
+ console.log(event.sendArgs);
+});
+onComplete(event => {
+ // sendArgs value is [1]
+ console.log(event.sendArgs);
+});
+
+// Send request
+send(1);
+```
+
+#### Received in force function
+
+force is used to specify whether to penetrate the response cache. The content about response cache will be explained in the [cache mode](/next/tutorial/cache/mode) later.
+
+```javascript
+const { send } = useRequest(alovaInstance.Get('/todo'), {
+ force: event => {
+ return event.sendArgs[0];
+ }
+});
+send(1);
+```
+
+### Download and upload progress
+
+Download and upload progress information can be obtained through `downloading` and `uploading`, and you can display the progress information directly in the view.
+
+```javascript
+const { downloading } = useRequest(alovaInstance.Get('/todo/downloadfile'));
+const { uploading } = useRequest(alovaInstance.Get('/todo/uploadingfile'));
+```
+
+The data format of `downloading` and `uploading` is as follows:
+
+```ts
+export type Progress = {
+ total: number;
+ loaded: number;
+};
+```
+
+### Abort request
+
+Use useHook to receive `abort` for manual abort request.
+
+```javascript
+const {
+ // ...
+ // highlight-start
+ // abort function is used to interrupt the request
+ abort
+ // highlight-end
+} = useRequest(todoListGetter);
+
+abort();
+```
+
+### Additional managed states
+
+Set additional managed states, which can be updated across components. For details, please refer to [Additional managed states](/next/tutorial/client/in-depth/manage-extra-states).
+
+### Middleware
+
+The `useRequest` middleware can control almost all behaviors of a request, for example, you can use it to block requests.
+
+```js
+let allowRequest = false;
+useRequest(todoListGetter, {
+ middleware(ctx, next) {
+ if (!allowRequest) {
+ return;
+ }
+ return next();
+ }
+});
+```
+
+For details, please refer to [In-depth Client-Middleware](/next/tutorial/client/in-depth/middleware).
+
+## API
+
+Please refer to [API-useRequest](/next/api/core-hooks#userequest).
diff --git a/docs/tutorial/03-client/01-strategy/02-use-watcher.md b/docs/tutorial/03-client/01-strategy/02-use-watcher.md
new file mode 100644
index 000000000..235f78d26
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/02-use-watcher.md
@@ -0,0 +1,147 @@
+---
+title: Watching Request
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import EmbedSandpack from "@site/src/components/EmbedSandpack";
+import CodeBlock from '@theme/CodeBlock';
+import useWatcherSearchVue from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/vueComposition-search.en.vue';
+import useWatcherSearchReact from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/react-search.en.jsx';
+import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox@3/02-client/02-use-watcher/svelte-search.en.svelte';
+
+:::info Strategy type
+
+use hook
+
+:::
+
+In some scenarios where you need to re-request as data changes, such as paging, data filtering, fuzzy search, tab bar switching, etc., you can use `useWatcher` to listen to the specified state change and send a request immediately.
+
+## Example
+
+Next, let's take the search for todo items as an example and try to change the options in the selection box to see how the todo list changes.
+
+
+
+
+
+
+
+
+
+
+
+
+
+{useWatcherSearchSvelte}
+
+
+
+
+## Usage
+
+:::tip Usage Tips
+
+useWatcher supports all the features of useRequest. For details, please see [useRequest](/next/tutorial/client/strategy/use-request). The following are the unique uses of useWatcher.
+
+:::
+
+### Send request immediately
+
+Unlike `useRequest`, the `immediate` property of `useWatcher` defaults to `false`.
+
+```javascript
+const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], {
+ // highlight-start
+ immediate: true
+ // highlight-end
+});
+send();
+```
+
+### Request debounce
+
+Usually we write debounce code at the level of frequently triggered events. This time we implemented the debounce function at the request level, which means you no longer need to implement debounce yourself in the fuzzy search function, and the usage is also very simple.
+
+:::info What is debounce?
+
+Debounce means that after an event is triggered, a function can only be executed once within n seconds. If another event is triggered within n seconds after the event is triggered, the function execution delay time will be recalculated (distinguished from throttling here. Throttling means that the event cannot be triggered again within a period of time after the event is triggered)
+
+:::
+
+**Set the debounce for all watching states**
+
+```javascript
+const { loading, data, error } = useWatcher(
+ () => filterTodoList(keyword, date),
+ [keyword, date],
+ {
+ // highlight-start
+ // Setting debounce to a number means the debounce time for all watching states in milliseconds
+ // For example, this means that when one or more of the states keyword and date change, the request will be sent after 500ms
+ debounce: 500
+ // highlight-end
+ }
+);
+```
+
+**Set the debounce for a single watching state**
+
+In many scenarios, we only need to debounce a few frequently changing watching states, such as the state change triggered by `onInput` of the text box. This can be done as follows:
+
+```javascript
+const { loading, data, error } = useWatcher(
+ () => filterTodoList(keyword, date),
+ [keyword, date],
+ {
+ // highlight-start
+ // Set the debounce time in the order of the array of watching states. 0 or no transmission means no debounce
+ // The order of the watching states here is [keyword, date], and the debounce array is set to [500, 0], which means that only the keyword is set to debounce
+ debounce: [500, 0]
+ // You can also set it as follows:
+ // debounce: [500],
+ // highlight-end
+ }
+);
+```
+
+### Request sequence
+
+Sometimes when the state watched by `useWatcher` changes continuously and causes continuous requests to be initiated, the latter request gets a response before the previous request, but when the previous request gets a response, it will overwrite the response of the latter request, resulting in a response that does not match the state; for example, if a state `state` changes and request `1` is issued, and then the `state` value is changed before request `1` is responded, and request `2` is issued, if request `1` returns after request `2`, the final response data will remain in request `1`.
+Therefore, we designed the `abortLast` parameter, which is used to mark whether to interrupt the last unresponsive request when the next request is sent. The default value is `true`, so that only the last request sent by `useWatcher` is valid.
+
+```mermaid
+sequenceDiagram
+ participant U as user
+ participant S as server
+ U ->> U: watch state
+ U ->> S: state changes and initiates request 1
+ U ->> S: state changes and initiates request 2
+ S ->> U: request 2 responds first
+ S ->> U: request 1 responds later
+ U ->> U: request 2 response is overwritten
+```
+
+```javascript
+useWatcher(
+ () => getTodoList($currentPage),
+ // Array of watched states, these state changes will trigger a request
+ [state],
+ {
+ // highlight-start
+ abortLast: true // Whether to interrupt the last unresponsive request, the default is true
+ // highlight-end
+ }
+);
+```
+
+:::warning Notes
+
+`abortLast` defaults to `true`. Under normal circumstances, you don't need to pay attention to this parameter. If it is changed to `false`, it may cause problems with state and response mismatch.
+
+:::
+
+## API
+
+Please refer to [API-useWatcher](/next/api/core-hooks#usewatcher).
diff --git a/docs/tutorial/03-client/01-strategy/03-use-fetcher.md b/docs/tutorial/03-client/01-strategy/03-use-fetcher.md
new file mode 100644
index 000000000..0568095cb
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/03-use-fetcher.md
@@ -0,0 +1,232 @@
+---
+title: Fetch Data
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info 策略类型
+
+use hook
+
+:::
+
+When you have the following needs:
+
+1. Preload the data that will be used in subsequent processes and store it in the cache, so that users no longer have to wait for the data loading process;
+2. Conveniently implement cross-page data update (similar to global state), for example, modify an item in the todo list and then re-fetch the latest data, and the interface will be refreshed after the response.
+
+`useFetcher` is the hook used to implement the above scenario. The response data obtained through it cannot be received directly, but the data fetched through it will not only update the cache, but also update the corresponding state, thereby re-rendering the view.
+
+## Preload data
+
+Let's implement a paging list to automatically preload the next page of data. Before preloading data, please make sure that the Method instance used has enabled caching.
+
+
+
+
+```html
+
+
Fetching...
+
+
+
+
+```
+
+
+
+
+```jsx
+import { useState } from 'react';
+import { useFetcher } from 'alova/client';
+
+//method instance creation function
+const getTodoList = currentPage => {
+ return alovaInstance.Get('/todo/list', {
+ cacheFor: 60000,
+ params: {
+ currentPage,
+ pageSize: 10
+ }
+ });
+};
+
+const App = () => {
+ const {
+ // the loading is the status of data fetching
+ loading,
+ error,
+ onSuccess,
+ onError,
+ onComplete,
+
+ // Only after calling fetch will a request be sent to fetch data. You can call fetch repeatedly to fetch data from different interfaces.
+ fetch
+ } = useFetcher({
+ updateState: false
+ });
+ const [currentPage, setCurrentPage] = useState(1);
+ const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], {
+ immediate: true
+ }).onSuccess(() => {
+ // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page.
+ fetch(getTodoList(currentPage + 1));
+ });
+
+ return (
+ <>
+ {loading ?
+{/if}
+
+```
+
+
+
+
+:::warning
+
+The above example is set `updateState` to false when calling `useFetcher`. This is because the data fetching will automatically trigger a states cross-component updating by default, causing the view to be re-rendered. When preload data is the same as the currently requested data. You can set it to false to avoid affecting view errors.
+
+:::
+
+## Update views across modules/components
+
+Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method snapshots matcher](/next/tutorial/client/in-depth/method-matcher) to dynamically fetch the data of the current page.
+
+> The Method snapshots matcher is used to find method instances that meet the conditions among the requested method instances.
+
+First, set a name for the method instance in the todo list, which is used to filter out the required Method instance when the Method instance cannot be specified directly.
+
+```javascript title="api/todoList.js"
+const getTodoList = currentPage => {
+ return alovaInstance.Get('/todo/list', {
+ // highlight-start
+ name: 'todoList',
+ // highlight-end
+ params: {
+ currentPage,
+ pageSize: 10
+ }
+ });
+};
+```
+
+Then in the `EditTodo` component, use the `fetch` function to dynamically find the last name of `todoList` in the requested Method instance to fetch data.
+
+```javascript title="EditTodo Component"
+const { fetch } = useFetcher();
+
+// Trigger data fetch in event
+const handleSubmit = () => {
+ // submit data...
+ // highlight-start
+ const lastMethod = alovaInstance.snapshots.match({
+ name: 'todoList',
+ filter: (method, index, ary) => {
+ // Return true to specify the Method instance that needs to be fetched
+ return index === ary.length - 1;
+ }
+ }, true);
+ if (lastMethod) {
+ await fetch(lastMethod);
+ }
+ // highlight-end
+};
+```
+
+:::warning Notes
+
+useFetcher only updates the cache after the request is completed, and if this Method is foundIf the instance has been requested using useHook before, the `data` state created by this useHook will also be updated to ensure that the page data is consistent. This is the guarantee that `useFetcher` is used to update views across modules/components.
+
+:::
+
+> For more methods of using `Method` instance matcher, see [Method instance matcher](/next/tutorial/client/in-depth/method-matcher).
+
+## Force sending request
+
+Same as `useRequest` and `useWatcher`, please read [Force Request](/next/tutorial/cache/force-request) for more information.
diff --git a/docs/tutorial/03-client/01-strategy/04-use-pagination.md b/docs/tutorial/03-client/01-strategy/04-use-pagination.md
new file mode 100644
index 000000000..e19d1da0e
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/04-use-pagination.md
@@ -0,0 +1,698 @@
+---
+title: Pagination request strategy
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info stragety type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with basic usage of alova.
+
+A hook designed for paging scenarios, which can help you automatically manage paging data, preload data, reduce unnecessary data refresh, improve fluency by 300%, and reduce coding difficulty by 50%\*\*. You can use it in the two paging scenarios of pull-down loading and page number flipping. This hook provides a wealth of features to help your application create better performance and more convenient paging functions.
+
+
+
+## Features
+
+- Rich and comprehensive paging status;
+- Rich and comprehensive pagination events;
+- Change page, pageSize to automatically get specified paging data;
+- Data caching, no need to repeatedly request list data of the same parameters;
+- Front and back pages are preloaded, no waiting for page turning;
+- Search condition monitoring automatically reacquires pages;
+- Support adding, editing and deleting list data;
+- Support refreshing the data of the specified page without reset;
+- Request-level search anti-shake, no need to maintain by yourself;
+
+## Usage
+
+### Render list data
+
+
+
+
+```html
+
+
+ {{ item.name }}
+
+
+
+
+ There are {{ pageCount }} pages
+ A total of {{ total }} data
+
+
+
+```
+
+
+
+
+```jsx
+import { queryStudents } from './api.js';
+import { usePagination } from 'alova/client';
+
+const App = () => {
+ const {
+ // loading state
+ loading,
+
+ // list data
+ data,
+
+ // is it the last page
+ // This parameter can be used to determine whether it needs to be loaded during pull-down loading
+ isLastPage,
+
+ // The current page number, changing this page number will automatically trigger the request
+ page,
+
+ // Number of data items per page
+ pageSize,
+
+ // number of paging pages
+ pageCount,
+
+ // total amount of data
+ total,
+
+ // update states
+ update
+ } = usePagination(
+ // Method instance acquisition function, it will receive page and pageSize, and return a Method instance
+ (page, pageSize) => queryStudents(page, pageSize),
+ {
+ // Initial data before the request (data format returned by the interface)
+ initialData: {
+ total: 0,
+ data: []
+ },
+ initialPage: 1, // initial page number, default is 1
+ initialPageSize: 10 // The initial number of data items per page, the default is 10
+ }
+ );
+
+ // Turn to the previous page, the request will be sent automatically after the page value changes
+ const handlePrevPage = () => {
+ update({
+ page: page - 1
+ });
+ };
+
+ // Turn to the next page, the request will be sent automatically after the page value changes
+ const handleNextPage = () => {
+ update({
+ page: page + 1
+ });
+ };
+
+ // Change the number of pages, the request will be sent automatically after the pageSize value is changed
+ const handleSetPageSize = () => {
+ update({
+ pageSize: 20
+ });
+ };
+
+ return (
+
+ {data.map(item => (
+
+ {item.name}
+
+ ))}
+
+
+
+ There are {pageCount} pages
+ A total of {total} pieces of data
+
+{/each}
+
+
+
+There are {pageCount} pages
+A total of {total} pieces of data
+```
+
+
+
+
+### Specify pagination data
+
+The data structure returned by each paging data interface is different, so we need to tell `usePagination` the list data and the total number of items separately, so as to help us manage the paging data.
+
+Suppose the data format returned by your paging interface is as follows:
+
+```typescript
+interface PaginationData {
+ totalNumber: number;
+ list: any[];
+}
+```
+
+At this point, you need to return the list data and the total number of items in the form of a function.
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize), {
+ //...
+ // highlight-start
+ total: response => response.totalNumber,
+ data: response => response.list
+ // highlight-end
+});
+```
+
+If you don't specify the total and data callback functions, they will get data in the following ways by default.
+
+```javascript
+const total = response => response.total;
+const data = response => response.data;
+```
+
+:::warning Caution
+
+The data callback function must return a list of data, indicating the data set used in paging, and total is mainly used to calculate the current page number. If no number is returned in the total callback function, it will pass whether the number of requested lists is less than the pageSize value To determine whether the current page is the last page, which is generally used for pull-down loading.
+
+:::
+
+### Enable append mode
+
+By default, the original list data will be replaced when the page is turned, and the append mode will append the data of the next page to the bottom of the current list when the page is turned. A common usage scenario is to pull down to load more.
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize), {
+ //...
+ // highlight-start
+ append: true
+ // highlight-end
+});
+```
+
+### Preload adjacent page data
+
+In order to provide a better experience for pagination, when the previous and next pages of the current page meet the conditions, it will be automatically preloaded, so that when the user turns the page, the data can be displayed directly without waiting. This is the default behavior. If you don't want to preload the data of adjacent pages, you can turn it off in the following way.
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize), {
+ //...
+ // highlight-start
+ preloadPreviousPage: false, // turn off preloading previous page data
+ preloadNextPage: false // turn off preloading next page data
+ // highlight-end
+});
+```
+
+:::warning preloading trigger conditions
+
+When preloading switch is turned on, the next page will not be loaded blindly. The following two conditions need to be met:
+
+1. Preloading is based on cache of alova. The method instance used for pagination must enable chache. By default, get requests will have 5 minutes of memory cache. If it is a non-get request or the cache is turned off globally, you also need to set the cache in this Method. In the instance, set `cacheFor` separately to enable cache.
+2. Based on the `total` and `pageSize` parameters, it is determined that there is still data on the next page.
+
+:::
+
+In addition to `onSuccess, onError, onComplete` request events, when preloading is triggered, you can also know the preloading status through `fetching`, and you can also listen to preloading request events through `onFetchSuccess, onFetchError, onFetchComplete`.
+
+```javascript
+const {
+ // preload state
+ fetching,
+
+ // preload success event binding function
+ onFetchSuccess,
+
+ // preload error event binding function
+ onFetchError,
+
+ // Preloading complete event binding function
+ onFetchComplete
+} = usePagination((page, pageSize) => queryStudents(page, pageSize), {
+ //...
+});
+```
+
+### Listening filter conditions
+
+In many cases, the list needs to be filtered by conditions. At this time, the re-request can be triggered through the status monitoring of `usePagination`, which is the same as `useWatcher` provided by alova.
+
+For example, filter by student name, student grade.
+
+
+
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+```jsx
+import { queryStudents } from './api.js';
+import { usePagination } from 'alova/client';
+
+const App = () => {
+ // search condition status
+ const [studentName, setStudentName] = useState('');
+ const [clsName, setClsName] = useState('');
+ const {
+ //...
+ } = usePagination(
+ (page, pageSize) => queryStudents(page, pageSize, studentName, clsName),
+ {
+ //...
+ // highlight-start
+ watchingStates: [studentName, clsName]
+ // highlight-end
+ }
+ );
+
+ return (
+ // highlight-start
+ setStudentName(target.value)} />
+
+ // highlight-end
+ //...
+ );
+};
+```
+
+
+
+
+```html
+
+
+
+
+
+
+
+```
+
+
+
+
+Same as `useWatcher`, you can also implement request debounce by specifying `debounce`, for details, please refer to [useWatcher's debounce parameter setting](/next/api/core-hooks#usewatcher).
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), {
+ //...
+ // highlight-start
+ debounce: 300 // Anti-shake parameters, in milliseconds, can also be set as an array to set the anti-shake time separately for watchingStates
+ // highlight-end
+});
+```
+
+It should be noted that `debounce` is achieved by request debounce in [**useWatcher**](/next/api/core-hooks#usewatcher). **At the end of the monitoring state, there are two hidden monitoring states of page and pageSize, which can also be set by debounce. **
+
+For example, when `watchingStates` is set to `[studentName, clsName]`, `[studentName, clsName, page, pageSize]` will be monitored internally, so if you need to set anti-shake for page and pageSize, you can specify ` [0, 0, 500, 500]`.
+
+### Close initialization request
+
+By default, `usePagination` will initiate a request during initialization, but you can also use `immediate` to turn it off, and then pass the `send` function, or change `page` or `pageSize`, and `watchingStates`, etc. state to initiate the request.
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), {
+ //...
+ // highlight-start
+ immediate: false
+ // highlight-end
+});
+```
+
+## List action functions
+
+usePagination provides a fully functional list action function, which can achieve the same effect as the re-request list without re-requesting the list, which greatly improves the interactive experience of the page. The specific function description continues to look down!
+
+### Insert list item
+
+You can use it to insert list items to any position in the list, and it will remove the last item after insertion to ensure the same effect as re-requesting the current page data.
+
+```typescript
+/**
+ * Insert data
+ * If no index is passed in, it will be inserted at the front by default
+ * If a list item is passed in, it will be inserted after the list item, and an error will be thrown if the list item is not in the list data
+ * @param item insert item
+ * @param indexOrItem insertion position (index)
+ */
+declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void;
+```
+
+The following is an example of returning to the first page and then inserting list items in **non-append mode** (page number flipping scenario):
+
+```javascript
+page.value = 1;
+nextTick(() => {
+ insert(newItem, 0);
+});
+```
+
+The following is an example of scrolling to the top after inserting a list item in **append mode** (drop-down loading scene):
+
+```javascript
+insert(newItem, 0);
+nextTick(() => {
+ window.scrollTo(0, {});
+});
+```
+
+You can also specify the second parameter of `insert` as a list item. When the same reference of this list item is found, the insert item will be inserted after this list item.
+
+```javascript
+insert(newItem, afterItem);
+```
+
+:::warning Caution
+
+In order to make the data correct, the insert function call will clear all caches.
+
+:::
+
+### Remove list item
+
+In the case that the next page has a cache, it will use the cache of the next page to add to the end of the list item after removing an item, so as to ensure the same effect as re-requesting the data of the current page. In **append mode** and Behave the same in **non-append mode**.
+
+```typescript
+/**
+ * Remove data
+ * If a list item is passed in, the list item will be removed, and an error will be thrown if the list item is not in the list data
+ * @param position index or list item to remove
+ */
+declare function remove(position: number | LD[number]): void;
+```
+
+You can also specify the second parameter of `remove` as a list item, and when the same reference of this list item is found, this list item will be removed.
+
+But in the following two cases, it will re-initiate the request to refresh the data of the corresponding page:
+
+1. The next page is not cached
+2. The data that exceeds the next page cache list item is continuously called synchronously, and the cache data is not enough to supplement the current page list.
+
+:::warning Caution
+
+In order to make the data correct, the remove function call will clear all caches.
+
+:::
+
+### Update data items
+
+Use this function when you want to update list items.
+
+```typescript
+/**
+ * Replace data
+ * If the position passed in is a list item, this list item will be replaced, if the list item is not in the list data, an error will be thrown
+ * @param item replacement item
+ * @param position replacement position (index) or list item
+ */
+declare function replace(
+ item: LD extends any[] ? LD[number] : any,
+ position: number | LD[number]
+): void;
+```
+
+You can also specify the second parameter of `replace` as a list item, which will be replaced when an identical reference to the list item is found.
+
+### Refresh the data of the specified page
+
+When you do not want to update the list items locally after the data operation, but re-request the data on the server side, you can use refresh to refresh the data on any page, without resetting the list data and letting the user start browsing from the first page again.
+
+```typescript
+/**
+ * Refresh the specified page number data, this function will ignore the cache to force the send request
+ * If no page number is passed in, the current page will be refreshed
+ * If a list item is passed in, the page where the list item is located will be refreshed
+ * @param pageOrItemPage Refreshed page number or list item
+ */
+declare function refresh(pageOrItemPage?: number | LD[number]): void;
+```
+
+In append mode, you can specify the parameter of `refresh` as a list item. When the same reference of this list item is found, the data of the page number where this list item is located will be refreshed.
+
+### Manually update list data
+
+Use the `update` function to update responsive data, which is similar to [useRequest's update](/next/tutorial/client/strategy/use-request), the only difference is that when calling `update` to update `data`, the list data is updated, while non-response data. This is useful when manually clearing list data without reissuing the request.
+
+```typescript
+// case list data
+update({
+ data: []
+});
+```
+
+### Reset list
+
+It will clear all caches and reload the first page.
+
+```typescript
+/**
+ * Reload the list from the first page and clear the cache
+ */
+declare function reload(): void;
+```
+
+## API
+
+### Hook configuration
+
+Inherit all configurations of [**useWatcher**](/next/api/core-hooks#usewatcher).
+
+| Name | Description | Type | Default | Version |
+| ------------------- | --------------------------------------------------------------------- | ------------------------- | -------------------------- | ------- |
+| initialPage | initial page number | number | 1 | - |
+| initialPageSize | Initial number of data items per page | number | 10 | - |
+| watchingStates | state monitoring trigger request, implemented using useWatcher | any[] | [page, pageSize] | - |
+| debounce | The debounce parameter of state monitoring, implemented by useWatcher | number \| number[] | - | - |
+| append | Whether to enable append mode | boolean | false | - |
+| data | Array data specifying pagination | (response: any) => any[] | response => response.data | - |
+| total | specify the total amount of data | (response: any) => number | response => response.total | - |
+| preloadPreviousPage | whether to preload previous page data | boolean | true | - |
+| preloadNextPage | whether to preload next page data | boolean | true | - |
+
+### Responsive data
+
+Inherit all responsive data from [**useWatcher**](/next/api/core-hooks#usewatcher).
+
+| Name | Description | Type | Version |
+| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- |
+| page | Current page number, determined by initialPage | number | - |
+| pageSize | The current number of pages, determined by initialPageSize | number | - |
+| data | paging list array data, obtained from data configuration | any[] | - |
+| total | The total amount of data, obtained from total configuration, can be empty | number | - |
+| pageCount | The total number of pages, calculated from total and pageSize | number | - |
+| isLastPage | Whether the current page is the last page, if pageCount has a value, it will be obtained by comparing pageCount and page, otherwise it will be obtained by whether the length of the list data is less than pagSize | number | - |
+| fetching | whether data is being preloaded | boolean | - |
+
+### Action function
+
+Inherit all action functions of [**useWatcher**](/next/api/core-hooks#usewatcher).
+
+| name | description | function parameters | return value | version |
+| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------- |
+| refresh | Refresh the data of the specified page number, this function will ignore the forced sending request of the cache, in the append mode, the list item can be passed in to indicate the page number where the list item is refreshed | pageOrItemPage: Refreshed page number or list item | - | - |
+| insert | Insert a piece of data. If no index is passed in, it will be inserted at the front by default. If a list item is passed in, it will be inserted after the list item. If the list item is not in the list data, an error will be thrown | 1. item: insert item 2. indexOrItem: insert position (index) or list item, default is 0 | - | - |
+| remove | Remove a piece of data. When a number is passed in, it means the removed index. When the position is passed in a list item, the list item will be removed. If the list item is not in the list data, an error will be thrown | position : remove position (index) or list item | - | - |
+| replace | Replace a piece of data. When the second parameter is passed in a number, it means the replacement index. A negative number means counting from the end. When the position passed in is a list item, the list item will be replaced. If the list item is not in the list data An error will be thrown | 1. item: replacement item 2. position: replacement position (index) or list item, when a negative number is passed in, it means counting from the end | - | - |
+| reload | Clear the data and re-request the first page of data | - | - | - |
+| update | Update state data, same as alova's use hook, but update list data when updating data field | newFrontStates: new state data object | - | - |
+
+### Event
+
+Inherit all events from [**useWatcher**](/next/api/core-hooks#usewatcher).
+
+| Name | Description | Callback Parameters | Version |
+| --------------- | ---------------------------------------------- | ------------------------------------ | ------- |
+| onFetchSuccess | fetch success callback binding function | event: alova success event object | - |
+| onFetchError | callback binding function for fetch failure | event: alova failure event object | - |
+| onFetchComplete | callback binding function for fetch completion | event: alova completion event object | - |
diff --git a/docs/tutorial/03-client/01-strategy/05-use-form.md b/docs/tutorial/03-client/01-strategy/05-use-form.md
new file mode 100644
index 000000000..030c0ef23
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/05-use-form.md
@@ -0,0 +1,487 @@
+---
+title: Form submiting strategy
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+A hook designed for form submission. Through this hook, you can easily implement form drafts and multi-page (multi-step) forms. In addition, it also provides common functions such as form reset.
+
+
+
+## Features
+
+- draft form;
+- Multi-page (multi-step) forms;
+- Form submission automatically resets data;
+- Reset form data manually;
+
+## Usage
+
+### Basic usage
+
+Demonstrates basic use of form hooks.
+
+
+
+
+```html
+
+
+
+
+
+
+
+```
+
+
+
+
+```jsx
+import { formSubmit } from './api.js';
+import { useForm } from 'alova/client';
+
+const App = () => {
+ const {
+ // submit status
+ loading: submitting,
+
+ // Responsive form data, the content is determined by initialForm
+ form,
+
+ // submit data function
+ send: submit,
+
+ // update form item
+ updateForm,
+
+ // Submit successful callback binding
+ onSuccess,
+
+ // Submit failure callback binding
+ onError,
+
+ // Submit completed callback binding
+ onComplete
+ } = useForm(
+ formData => {
+ // Form data can be converted and submitted here
+ return formSubmit(formData);
+ },
+ {
+ // Initialize form data
+ initialForm: {
+ name: '',
+ cls: '1'
+ }
+ }
+ );
+
+ // submit form data
+ const handleSubmit = () => {
+ // Validate form data...
+ submit();
+ };
+
+ return (
+
+ updateForm({ name: target.value })}
+ />
+
+
+
+ );
+};
+```
+
+
+
+
+```html
+
+
+
+
+
+```
+
+
+
+
+`useForm` will not request by default, and the request will be sent after calling `send`. At the same time, the callback function of `useForm` will pass in the latest form data. If you need to convert the data before submitting, you can convert it here, or Can be converted in the `formSubmit` function.
+
+:::warning Caution
+
+1. `initialForm` is to set the initial form data, `initialData` is to set the initial response data, pay attention to the distinction;
+2. `updateForm` is to update the form data, and `update` is to update the response data, pay attention to the distinction;
+
+:::
+
+The above example only shows a simple form submission function, there is no difference between ordinary form submissions, but `useForm` also implements more practical functions, let us continue to look down.
+
+### Submit auto reset form
+
+Many times, we need to reset the form data after the form is submitted. We always need to manually reassign values one by one when implementing it ourselves, and `useForm` can help us do it automatically.
+
+```javascript
+useForm(submitData, {
+ //...
+ // highlight-start
+ // Set this parameter to true to automatically reset the form data after submission
+ resetAfterSubmitting: true
+ // highlight-end
+});
+```
+
+If you need to manually reset the form data, you can also do it by calling the `reset` function.
+
+```javascript
+const {
+ // highlight-start
+ // form reset function
+ reset
+ // highlight-end
+} = useForm(submitData, {
+ //...
+});
+
+// highlight-start
+const handleReset = () => {
+ reset();
+};
+// highlight-end
+```
+
+### Update form data
+
+When editing a form, we need to display the data of the original form. At this time, we can use `updateForm` to asynchronously update the form data.
+
+```javascript
+const {
+ // ...
+ updateForm
+} = useForm(submitData, {
+ // ...
+ {
+ //Initialize form data
+ initialForm: {
+ name: '',
+ cls: '1'
+ }
+ }
+});
+
+// Request form data and update it to the form
+const { onSuccess } = useRequest(getData);
+onSuccess(({ data }) => {
+ updateForm({
+ name: data.name,
+ cls: data.cls
+ });
+});
+```
+
+### Form draft
+
+`useForm` also provides a form draft function, even if the page is refreshed before the data is reset, the form data can be restored. The principle is to use the storage adapter on the alova instance to persist the form data. You only need to set `store` to true when using it.
+
+```javascript
+useForm(submitData, {
+ //...
+ // highlight-start
+ // Turn on persistence to save data. After setting to true, uncommitted data will be persisted in real time
+ store: true
+ // highlight-end
+});
+```
+
+Before the data is persisted, `JSON.stringify` will be called to convert it into a JSON string. By default, the form data will be serialized when it is persisted. `useForm` has built-in `Date` and `RegExp` instances , which will be useful when using timepickers.
+
+In the form data only involves `Date` and `RegEYou don’t need to do more for xp` instances, but if there are other non-JSON data, such as `moment` instances, we need to customize the serializer, but don’t worry, the custom serializer is very simple, the following will show the settings A `moment` serializer.
+
+```javascript
+import moment from 'moment';
+const momentSerializer = {
+ // forward is called when serializing
+ // Need to judge whether it is a moment instance, if it is not the target value, return undefined, indicating that it will not be processed
+ forward: data => moment.isMoment(data) ? data.valueOf() : undefined,
+
+ // backward is called during deserialization, data is the value returned in forward
+ backward: timestamp => moment(timestamp);
+};
+
+useForm(
+ submitData,
+ {
+ store: {
+ enable: true,
+ serializers: {
+ moment: momentSerializer
+ }
+ }
+ }
+);
+```
+
+### Multi-page/multi-step forms
+
+Many times we encounter situations where form items are divided into multiple pages, or filled in multiple steps, and submitted in a unified manner at the end, such as multi-step user registration, questionnaire filling, etc., and forms with multiple steps may have interdependence Relationship, if realized by itself will bring some trouble. And `useForm` realizes form data sharing, you can get the same form data in different pages or components, which solves the problem of multi-step form data dependence, and does not need to summarize form data when submitting, and can submit directly.
+
+When using, you need to set the id through `useForm`, and you can share the same form data between different pages with the same id. For example, we have a form that needs to go through 3 steps to fill out the form, and they will go through component A, component B, and component C respectively.
+
+```
+Component A -> Component B -> Component C
+```
+
+At this point, we can initialize the form data inside component A:
+
+```javascript title=Component A
+const returnStates = useForm(submitData, {
+ initialForm: {
+ step1Input: '',
+ step2Input: '',
+ step3Input: ''
+ },
+ // highlight-start
+ id: 'testForm'
+ // highlight-end
+});
+const { form, send } = returnStates;
+```
+
+In component B and component C, you can get the shared data by specifing the same id in component A.
+
+```javascript title=Component B, Component C
+const returnStates = useForm(submitData, {
+ id: 'testForm'
+});
+const { form, send } = returnStates;
+```
+
+The `returnStates` returned by id in components B and C are the same reference as the `returnStates` in component A. You can use the same `form`, or you can call `send` in any component to submit the form data uniformly.
+
+**additional**
+
+If your multi-step form is not in a certain order, but in random order according to certain conditions, for example:
+
+```bash
+# possible order 1
+Component B -> Component A -> Component C
+
+# possible order 2
+Component A -> Component C -> Component B
+
+# possible order 3
+Component C -> Component A -> Component B
+
+#...
+```
+
+In this case, you can set the same `useForm` config as component A in component B and C.
+
+```javascript title=Component B, Component C
+const returnStates = useForm(submitData, {
+ initialForm: {
+ step1Input: '',
+ step2Input: '',
+ step3Input: ''
+ },
+ id: 'testForm'
+});
+```
+
+In this way, no matter which component is rendered first, the form with the id of testForm can be initialized, and the subsequent components will first use the initialized form data when encountering the id of testForm, and will not initialize again. This way you can initialize form data inside any component.
+
+
+
+### Conditional filter
+
+`useForm` can also be used in the filtering form used in data filtering scenarios, for example, if you want to search city information by city name, you can set `immediate=true`, it will start querying data at initialization, and then In the operation, call `send` to repeatedly query the data.
+
+```javascript
+const { send: searchData } = useForm(queryCity, {
+ initialForm: {
+ cityName: ''
+ },
+ immediate: true
+});
+```
+
+:::warning Conditional Restrictions
+
+In conditional filtering scenarios, `useForm` is more suitable for non-paginated list conditional queries. If you need to perform conditional queries in paginated lists, it is recommended to use [Pagination Request Strategy (usePagination)](/next/tutorial/client/strategy/use-pagination).
+
+:::
+
+## API
+
+### Hook configuration
+
+Inherit all configurations from [**useRequest**](/next/api/core-hooks#userequest).
+
+| Name | Description | Type | Default | Version |
+| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------- | ------- |
+| initialForm | initial form data | any | - | - |
+| id | form id, the data data of the same id is the same reference, which can be used to share the same form data in multi-page forms. Single page form does not need to specify id | string \| number | - | - |
+| store | Whether to save data persistently, after setting to true, uncommitted data will be persisted in real time | boolean \| [StoreDetailConfig](#storedetailconfig) \| false | - |
+| resetAfterSubmiting | reset data after submission | boolean | false | - |
+
+### Responsive data
+
+Inherit all responsive data from [**useRequest**](/next/api/core-hooks#userequest).
+
+| Name | Description | Type | Version |
+| ---- | ----------------------------------- | ---- | ------- |
+| form | form data,determined by initialForm | any | - |
+
+#### StoreDetailConfig
+
+| Name | Description | Type | Default | Version |
+| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ------- |
+| enable | Whether to enable persistent data | boolean | required | - |
+| serializers | A collection of custom serializers, built-in serializers: 1. The date serializer is used to convert dates 2. The regexp serializer is used to convert regular expressions Yes Override the built-in serializer by setting the serializer with the same name | Record\ | - | - |
+
+#### DataSerializer
+
+| Name | Description | Type | Default | Versionthis |
+| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ----------- |
+| forward | Serialization function, when serializing in forward, it needs to judge whether it is the specified data, and return the converted data, otherwise return undefined or not return | (data: any) => any \| undefined \| void | required | - |
+| backward | deserialization function, deserialization data directly | (data: any) => any \| undefined \| void | required | - |
+
+### Action function
+
+Inherit all action functions of [**useRequest**](/next/api/core-hooks#userequest).
+
+| name | description | function parameters | return value | version |
+| ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------ | ------- |
+| updateForm | Update one or more form data | newForm: Partial\ \| (oldForm: F) => F) F is `initialForm` type | - | - |
+| reset | Reset to initialized data, if there is persistent data, it will also be cleared | - | - | - |
+
+### Event
+
+Inherit all events from [**useRequest**](/next/api/core-hooks#userequest).
+
+| Name | Description | Callback Parameters | Version |
+| --------- | ----------------------------------------------- | ------------------- | ------- |
+| onRestore | Triggered after the persistent data is restored | - | - |
diff --git a/docs/tutorial/03-client/01-strategy/06-token-authentication.md b/docs/tutorial/03-client/01-strategy/06-token-authentication.md
new file mode 100644
index 000000000..041f13429
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/06-token-authentication.md
@@ -0,0 +1,535 @@
+---
+title: Token authentication interceptor
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+Interceptor
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic use of alova.
+
+Token authentication interceptor provides unified management of token-based login, logout, token assignment, and token refresh, and supports silent token refresh.
+
+## Features
+
+- Unified maintenance of all codes for token identity authentication, including login, logout, token assignment, token refresh, etc.;
+- Supports verification of token expiration on the client and server, and refreshes the token without any warning;
+- Requests that rely on tokens automatically wait for the token refresh to complete before requesting;
+- Set request ID with metadata;
+- Automatically release visitor requests that do not rely on tokens;
+
+## Bind Token authentication interceptor
+
+Token identity authentication is completed through global interceptors, which provide `createClientTokenAuthentication` and `createServerTokenAuthentication` for client- and server-based identity authentication respectively.
+
+- Client-based identity authentication: means judging whether the token has expired from the client, such as the token expiration time obtained during login;
+- Server-based identity authentication: It indicates whether the token has expired based on the status returned from the server. For example, when `status` is 401, it means it has expired;
+
+### Bind client-based authentication interceptor
+
+```javascript
+import { createClientTokenAuthentication } from 'alova/client';
+import { createAlova } from 'alova';
+
+const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({
+ // ...
+});
+
+const alovaInstance = createAlova({
+ // ...
+ beforeRequest: onAuthRequired(method => {
+ // ...interceptor before original request
+ }),
+ responded: onResponseRefreshToken((response, method) => {
+ //...original response success interceptor
+ return response.json();
+ })
+});
+```
+
+In `onResponseRefreshToken`, you can also bind response error and completion interceptors, which is the same as the original usage.
+
+```javascript
+createAlova({
+ // ...
+ // highlight-start
+ responded: onResponseRefreshToken({
+ onSuccess: (response, method) => {
+ //...original response success interceptor
+ },
+ onError: (error, method) => {
+ //...original response error interceptor
+ },
+ onComplete: method => {
+ //...original response completion interceptor
+ }
+ })
+ // highlight-end
+});
+```
+
+If you don't need to set an interceptor, you don't need to pass in the interceptor function.
+
+```javascript
+createAlova({
+ //...
+ // highlight-start
+ beforeRequest: onAuthRequired(),
+ responded: onResponseRefreshToken()
+ // highlight-end
+});
+```
+
+### Bind server-based authentication interceptor
+
+Same as client-based usage
+
+```javascript
+import { createServerTokenAuthentication } from 'alova/client';
+import { createAlova } from 'alova';
+
+const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({
+ // ...
+});
+
+const alovaInstance = createAlova({
+ // ...
+ beforeRequest: onAuthRequired(method => {
+ // ...interceptor before original request
+ }),
+ responded: onResponseRefreshToken((response, method) => {
+ //...original response success interceptor
+ return response.json();
+ })
+});
+```
+
+:::warning
+
+When you use the `alova/fetch` adapter, you may encounter the problem `TypeError: Failed to execute 'json' on 'Response': body stream already read`. This is because the `body stream` of `Response` can only be accessed once, you can `response.clone().json()` to solve it.
+
+:::
+
+## Refresh Token silently on the client
+
+Just set `refreshToken` and specify whether the token expires, and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete.
+
+```javascript
+createClientTokenAuthentication({
+ refreshToken: {
+ // Triggered before the request, the method parameter will be received and a boolean will be returned to indicate whether the token has expired.
+ isExpired: method => {
+ return tokenExpireTime < Date.now();
+ },
+
+ // Triggered when the token expires, trigger the refresh token in this function
+ handler: async method => {
+ try {
+ const { token, refresh_token } = await refreshToken();
+ localStorage.setItem('token', token);
+ localStorage.setItem('refresh_token', refresh_token);
+ } catch (error) {
+ // redirect to login page once token refresh failed.
+ location.href = '/login';
+ // and must throw error.
+ throw error;
+ }
+ }
+ }
+});
+```
+
+:::warning Attention
+
+1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata.
+2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request.
+
+:::
+
+> For more information about metadata, go to [method metadata](/next/tutorial/getting-started/basic/method-metadata).
+
+```javascript
+export const refreshToken = () => {
+ const method = alovaInstance.Get('/refresh_token');
+ method.meta = {
+ authRole: 'refreshToken'
+ };
+ return method;
+};
+```
+
+## Refresh Token silently on the server side
+
+The same as refreshing the token silently on the client, just specify whether the token expires and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete.
+
+### Processed in request success interceptor
+
+When using `alova/fetch`, as long as the server returns the response data, the response success interceptor will be triggered. At this time, we need to handle the token refresh in the response success interceptor.
+
+```javascript
+createServerTokenAuthentication({
+ refreshTokenOnSuccess: {
+ // Triggered when responding, the response and method can be obtained, and a boolean is returned to indicate whether the token has expired.
+ // When the server returns 401, it means the token has expired.
+ isExpired: (response, method) => {
+ return response.status === 401;
+ },
+
+ // Triggered when the token expires, trigger the refresh token in this function
+ handler: async (response, method) => {
+ try {
+ const { token, refresh_token } = await refreshToken();
+ localStorage.setItem('token', token);
+ localStorage.setItem('refresh_token', refresh_token);
+ } catch (error) {
+ // redirect to login page once token refresh failed.
+ location.href = '/login';
+ // and must throw error.
+ throw error;
+ }
+ }
+ }
+});
+```
+
+### Handled in request error interceptor
+
+When using the `axios` interceptor, if the server returns a status code other than `200/300`, the response error interceptor will be triggered. At this time, we need to handle the token refresh in the response error interceptor.
+
+```javascript
+createServerTokenAuthentication({
+ refreshTokenOnError: {
+ // Triggered when responding, error and method can be obtained, and boolean is returned to indicate whether the token has expired.
+ // When the server returns 401, it means the token has expired.
+ isExpired: (error, method) => {
+ return error.response.status === 401;
+ },
+
+ // Triggered when the token expires, trigger the refresh token in this function
+ handler: async (error, method) => {
+ try {
+ const { token, refresh_token } = await refreshToken();
+ localStorage.setItem('token', token);
+ localStorage.setItem('refresh_token', refresh_token);
+ } catch (error) {
+ // redirect to login page once token refresh failed.
+ location.href = '/login';
+ // and must throw error.
+ throw error;
+ }
+ }
+ }
+});
+```
+
+:::warning Attention
+
+1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata.
+2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request.
+
+:::
+
+> For more information about metadata, please go to [method metadata](/next/tutorial/getting-started/basic/method-metadata).
+
+```javascript
+export const refreshToken = () => {
+ const method = alovaInstance.Get('/refresh_token');
+ method.meta = {
+ authRole: 'refreshToken'
+ };
+ return method;
+};
+```
+
+## Release visitor request
+
+Some interfaces do not need to rely on token authentication. We call them "guest requests". At this time, we can set their metadata to `authRole: null` to bypass the front-end interception and allow them to send requests and receive responses smoothly.
+
+```javascript
+export const requestTokenNotRequired = () => {
+ const method = alovaInstance.Get('/token_not_required');
+ method.meta = {
+ authRole: null
+ };
+ return method;
+};
+```
+
+## Login interception
+
+In the identity authentication interceptor, you can also intercept login requests and save login information in the interceptor to achieve the purpose of unified maintenance of identity authentication codes.
+
+First identify the metadata of the login request as `authRole: 'login'`.
+
+```javascript
+export const login = () => {
+ const method = alovaInstance.Get('/login');
+ method.meta = {
+ authRole: 'login'
+ };
+ return method;
+};
+```
+
+Then save the login information in the login interceptor.
+
+```javascript
+createClientTokenAuthentication({
+ login(response, method) {
+ localStorage.setItem('token', response.token);
+ localStorage.setItem('refresh_token', response.refresh_token);
+ }
+});
+```
+
+> The login interceptor usage of `createServerTokenAuthentication` is the same.
+
+## Assign token
+
+Usually, we will append token to the request information in `beforeRequest`. The `assignToken` callback function is provided in the Token authentication interceptor for assigning tokens. It will filter guest requests and login requests and trigger them before the requests. It can also achieve the purpose of unified maintenance of identity authentication codes.
+
+```javascript
+createClientTokenAuthentication({
+ assignToken: method => {
+ method.config.headers.Authorization = localStorage.getItem('token')};
+ }
+});
+```
+
+> The usage of assignToken callback function of `createServerTokenAuthentication` is the same.
+
+## Logout interception
+
+When your logout also requires calling the interface, you can also intercept the logout request and clear the login information.
+
+First identify the logout request metadata as `authRole: 'logout'`.
+
+```javascript
+export const logout = () => {
+ const method = alovaInstance.Get('/logout');
+ method.meta = {
+ authRole: 'logout'
+ };
+ return method;
+};
+```
+
+Then clear the login information in the logout interceptor.
+
+```javascript
+createClientTokenAuthentication({
+ logout(response, method) {
+ localStorage.removeItem('token');
+ localStorage.removeItem('refresh_token');
+ }
+});
+```
+
+> The login interceptor usage of `createServerTokenAuthentication` is the same.
+
+## Custom identification identity
+
+The above metadata identities are actually the default identities. If you need to customize the identity, you can set it as follows.
+
+### token refresh identity
+
+```javascript
+createClientTokenAuthentication({
+ refreshToken: {
+ // highlight-start
+ metaMatches: {
+ refreshToken: true
+ }
+ // highlight-end
+ // ...
+ }
+});
+```
+
+```javascript
+createServerTokenAuthentication({
+ refreshTokenOnSuccess: {
+ // highlight-start
+ metaMatches: {
+ refreshToken: true
+ }
+ // highlight-end
+ // ...
+ },
+ refreshTokenOnError: {
+ // highlight-start
+ metaMatches: {
+ refreshToken: true
+ }
+ // highlight-end
+ // ...
+ }
+});
+```
+
+Then, requests with `refreshToken: true` in the metadata will be identified as `refreshToken`.
+
+```javascript
+export const refreshToken = () => {
+ const method = alovaInstance.Get('/refresh_token');
+ method.meta = {
+ refreshToken: true
+ };
+ return method;
+};
+```
+
+### Visitor identify
+
+```javascript
+createClientTokenAuthentication({
+ visitorMeta: {
+ isVisitor: true
+ }
+});
+```
+
+Then, requests with `isVisitor: true` in the metadata will be identified as visitors.
+
+```javascript
+export const requestTokenNotRequired = () => {
+ const method = alovaInstance.Get('/token_not_required');
+ method.meta = {
+ isVisitor: true
+ };
+ return method;
+};
+```
+
+### Login identity
+
+```javascript
+createClientTokenAuthentication({
+ login: {
+ // highlight-start
+ metaMatches: {
+ login: true
+ },
+ // highlight-end
+ handler(response, method) {
+ //Login interceptor
+ }
+ }
+});
+```
+
+Then, requests with `login: true` in the metadata will be identified as `login` identity.
+
+```javascript
+export const login = () => {
+ const method = alovaInstance.Get('/login');
+ method.meta = {
+ login: true
+ };
+ return method;
+};
+```
+
+### Log out identity
+
+```javascript
+createClientTokenAuthentication({
+ logout: {
+ // highlight-start
+ metaMatches: {
+ logout: true
+ },
+ // highlight-end
+ handler(response, method) {
+ //Logout interceptor
+ }
+ }
+});
+```
+
+Then, requests with `logout: true` in the metadata will be identified as `logout`.
+
+```javascript
+export const logout = () => {
+ const method = alovaInstance.Get('/logout');
+ method.meta = {
+ logout: true
+ };
+ return method;
+};
+```
+
+> The login interceptor usage of `createServerTokenAuthentication` is the same.
+
+## Typescript
+
+By default, `createClientServerTokenAuthentication` and `createServerTokenAuthentication` are adapted to the `alova/fetch` request adapter, you can only specify the type of `StatesHook`, as follows:
+
+```typescript
+// highlight-start
+const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication<
+ typeof VueHook
+>({
+ // highlight-end
+ //...
+});
+
+const alovaInstance = createAlova({
+ // ...
+ beforeRequest: onAuthRequired(method => {
+ // The type of method is Method
+ }),
+ responded: onResponseRefreshToken((response, method) => {
+ //The response type is Response
+ return response.json();
+ })
+});
+```
+
+If you are not using the `alova/fetch` request adapter, You also need to specify the type of request adapter, which is also simple.
+
+The following is an example of the axios request adapter. Specify the request adapter type in `createClientTokenAuthentication`.
+
+```typescript
+import { axiosRequestAdapter } from '@alova/adapter-axios';
+
+// highlight-start
+const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication<
+ typeof ReactHook,
+ typeof axiosRequestAdapter
+>({
+ // highlight-end
+ //...
+});
+const alovaInstance = createAlova({
+ //...
+ // highlight-start
+ beforeRequest: onAuthRequired(method => {
+ // The type of method is Method
+ // highlight-end
+ }),
+ // highlight-start
+ responded: onResponseRefreshToken((response, method) => {
+ //The response type is AxiosResponse
+ // highlight-end
+ return response.data;
+ })
+});
+```
+
+The server-based Token authentication interceptor is used in the same way.
+
+```typescript
+import { axiosRequestAdapter } from '@alova/adapter-axios';
+
+// highlight-start
+createServerTokenAuthentication({
+ // highlight-end
+ //...
+});
+```
diff --git a/docs/tutorial/03-client/01-strategy/07-use-auto-request.md b/docs/tutorial/03-client/01-strategy/07-use-auto-request.md
new file mode 100644
index 000000000..2a3941c65
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/07-use-auto-request.md
@@ -0,0 +1,131 @@
+---
+title: Automatically refetch data
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic use of alova.
+
+Automatically fetch data through browser events or polling, allowing the interface to display the newest data.
+
+## Features
+
+- Supports refetching the newest data in scenarios such as browser focus, tab switching, network reconnection, polling requests, etc;
+- Supports request throttling, only one request will be sent if triggered multiple times in a short period of time;
+- Support custom event listening functions to adapt to usage scenarios in non-browser environments;
+
+## Basic usage
+
+By default, useHook`useAutoRequest` that automatically fetches data will automatically fetch the newest data when browser is visible, hidden, focused, and the network is reconnected, and will automatically cancel the listening event when the component is uninstalled.
+
+```javascript
+import { useAutoRequest } from 'alova/client';
+
+const { loading, data, error } = useAutoRequest(() => method());
+```
+
+The return value of `useAutoRequest` is the same as [useRequest](/next/api/core-hooks#userequest).
+
+In addition to supporting all configuration parameters of [useRequest](/next/api/core-hooks#userequest), it also supports automatically fetched configuration parameters. You can turn on or off some events through the following configuration, or modify request throttling events.
+
+```javascript
+const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest(
+ () => method(),
+ {
+ /**
+ * Browser display hide trigger
+ * @default true
+ */
+ enableVisibility: true,
+
+ /**
+ * Triggered by browser focus
+ * @default true
+ */
+ enableFocus: true,
+
+ /**
+ * Triggered by network reconnection
+ * @default true
+ */
+ enableNetwork: true,
+
+ /**
+ *Throttling time, only one request will be sent if triggered multiple times within a certain period, unit ms
+ * @default 1000
+ */
+ throttle: 1000,
+
+ /**
+ * The time of polling request, effective when set greater than 0, unit ms
+ * @default 0
+ */
+ pollingTime: 2000
+
+ //Other parameters are the same as useRequest...
+ }
+);
+```
+
+:::warning caching advice
+
+It is recommended to turn off the cache of the corresponding request when using `useAutoRequest`, because when the cache is set, the cache will be hit when the automatic request is triggered and the newest data cannot be obtained. Please read [Cache Mode](/next/tutorial/cache/mode) for details.
+
+:::
+
+## Custom listening functions
+
+The above 4 methods of automatically fetching data are implemented by listening browser's events by default. When users use it in a non-browser environment, you may need to customize the listening function. This function receives the notification request function and useHook config as parameters, and returns a cancel listening function.
+.
+
+The following is an example of custom listening function in `react-native`:
+
+### Network reconnection custom function
+
+```javascript
+import NetInfo from '@react-native-community/netinfo';
+useAutoRequest.onNetwork = (notify, config) => {
+ const unsubscribe = NetInfo.addEventListener(({ isConnected }) => {
+ isConnected && notify();
+ });
+ return unsubscribe;
+};
+```
+
+### Polling custom function
+
+```javascript
+useAutoRequest.onPolling = (notify, config) => {
+ const timer = setInterval(notify, config.pollingTime);
+ return () => clearInterval(timer);
+};
+```
+
+### App switching custom function
+
+```javascript
+import { AppState, Text } from 'react-native';
+useAutoRequest.onVisibility = (notify, config) => {
+ const subscription = AppState.addEventListener('change', state => {
+ state === 'active' && notify();
+ });
+ return () => subscription.remove();
+};
+```
+
+### App focus custom function
+
+Since the App doesn't have a focus event, it can be set to an empty function to avoid throwing error.
+
+```javascript
+useAutoRequest.onFocus = (notify, config) => {
+ return () => {};
+};
+```
diff --git a/docs/tutorial/03-client/01-strategy/08-action-delegation-middleware.md b/docs/tutorial/03-client/01-strategy/08-action-delegation-middleware.md
new file mode 100644
index 000000000..02e1df984
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/08-action-delegation-middleware.md
@@ -0,0 +1,204 @@
+---
+title: Cross components to trigger request
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+middleware
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+In the past, if you want to trigger a request in another component in one component, you need to save the data in the Store and complete it by dispatching Action. Now, you can use this middleware to **remove the limitation of component hierarchy**, and quickly trigger any request action function in any component.
+
+For example, after updating the menu data in a component, you can re-trigger the re-request of the side menu bar to refresh the data. When the list data is manipulated, the list update is triggered.
+
+
+
+## Features
+
+- Delegate the action function of any use hook in alova;
+- Trigger the delegated action function anywhere;
+
+## Usage
+
+### Basic usage
+
+> Take vue3 as an example, the usage is the same in react and svelte.
+
+Use `actionDelegationMiddleware` in component A to delegate the action function of `useRequest`.
+
+```javascript title=Component A
+import { actionDelegationMiddleware } from 'alova/client';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('actionName')
+});
+```
+
+In any component (such as component B), pass in the specified delegate name through `accessAction` to trigger the action function of `useRequest` in component A.
+
+```javascript title=Component B
+import { accessAction } from 'alova/client';
+
+accessAction('actionName', delegatedActions => {
+ // Call the send function in component A
+ delegatedActions.send();
+
+ // Call the abort function in component A
+ delegatedActions.abort();
+});
+```
+
+:::info note
+
+1. All use hooks in alova support action function delegation, but the functions delegated by different use hooks are different.
+2. When using `actionDelegationMiddleware`, the delegate name can be passed in strings, numbers, and symbol values.
+
+:::
+
+### Batch trigger action function
+
+In the above example, we use `accessAction` to trigger the action function of a use hook, but in fact, delegates with the same name will not override each other, but will be stored in a group, and we can use this name to trigger them at the same time The delegated function.
+
+```javascript title=Component C
+import { actionDelegationMiddleware } from 'alova/client';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('actionName1')
+});
+```
+
+```javascript title=Component D
+import { actionDelegationMiddleware } from 'alova/client';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('actionName1')
+});
+```
+
+```javascript title=Component E
+import { accessAction } from 'alova/client';
+
+// Because the delegate hooks of component C and component D will be matched, the callback function will be executed twice
+accessAction('actionName1', delegatedActions => {
+ // Call the send function in component C and component D
+ delegatedActions.send();
+
+ // Call the abort function in component C and component D
+ delegatedActions.abort();
+});
+```
+
+At the same time, regular expressions can also be used in `accessAction` to trigger batches of action functions whose delegate names meet the conditions
+
+```javascript title=Component F
+import { actionDelegationMiddleware } from 'alova/client';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('prefix_name1')
+});
+```
+
+```javascript title=Component G
+import { actionDelegationMiddleware } from 'alova/client';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('prefix_name2')
+});
+```
+
+```javascript title=Component H
+import { accessAction } from 'alova/client';
+
+// Because the delegate hooks of component F and component G will be matched, the callback function will be executed twice
+accessAction(/^prefix_/, delegatedActions => {
+ // Call the send function in component F and component G
+ delegatedActions.send();
+
+ // Call the abort function in component F and component G
+ delegatedActions.abort();
+});
+```
+
+## Action function delegation list
+
+Although the action functions delegated by most hooks are the same as the action functions themselves, this is not absolute. The following is the action function delegation list of each hook.
+
+### useRequest
+
+| name | description | function parameters | return value | version |
+| ------ | ------------------------------------------------------------ | ------------------- | ------------ | ------- |
+| send | Same as [useRequset](/next/api/core-hooks#userequest).send | | | - |
+| abort | Same as [useRequset](/next/api/core-hooks#userequest).abort | | | - |
+| update | Same as [useRequset](/next/api/core-hooks#userequest).update | | | - |
+
+### useWatcher
+
+Same as [useRequest delegate list](#userequest).
+
+### useFetcher
+
+| name | description | function parameters | return value | version |
+| ------ | ------------------------------------------------------------ | ------------------- | ------------ | ------- |
+| fetch | Same as [useFetcher](/next/api/core-hooks#usefetcher).fetch | | | - |
+| abort | Same as [useFetcher](/next/api/core-hooks#usefetcher).abort | | | - |
+| update | Same as [useFetcher](/next/api/core-hooks#usefetcher).update | | | - |
+
+### usePagination
+
+| name | description | function parameters | return value | version |
+| -------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------ | ------- |
+| refresh | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - |
+| insert | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - |
+| remove | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - |
+| replace | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - |
+| reload | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - |
+| update | For details, see [usePagination action function](/next/tutorial/client/strategy/use-pagination#api) | | | - |
+| getState | Get paging related data by name | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | Corresponding to the value of the statekey | - |
+
+### useSQRequest
+
+Same as [useRequest delegate list](#userequest).
+
+### useForm
+
+| name | description | function parameters | return value | version |
+| ---------- | --------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- |
+| updateForm | For details, see [useForm action function](/next/tutorial/client/strategy/use-form#api) | | | - |
+| reset | For details, see [useForm action function](/next/tutorial/client/strategy/use-form#api) | | | - |
+| send | Same as [useRequset](/next/api/core-hooks#userequest).send | | | - |
+| abort | Same as [useRequset](/next/api/core-hooks#userequest).abort | | | - |
+| update | Same as [useRequset](/next/api/core-hooks#userequest).update | | | - |
+
+### useCaptcha
+
+Same as [useRequest delegate list](#userequest).
+
+### useRetriableRequest
+
+| name | description | function parameters | return value | version |
+| ------ | --------------------------------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- |
+| stop | See [useRetriableRequest action function](/next/tutorial/client/strategy/use-retriable-request#api) for details | | | - |
+| send | Same as [useRequset](/next/api/core-hooks#userequest).send | | | - |
+| abort | Same as [useRequset](/next/api/core-hooks#userequest).abort | | | - |
+| update | Same as [useRequset](/next/api/core-hooks#userequest).update | | | - |
+
+### useSerialRequest
+
+Same as [useRequest delegate list](#userequest).
+
+### useSerialWatcher
+
+Same as [useRequest delegate list](#userequest).
diff --git a/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md
new file mode 100644
index 000000000..5c1458be4
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/02-virtual-data.md
@@ -0,0 +1,176 @@
+---
+title: Virtual data
+---
+
+In fact, virtual data is a reference object with a unique id, and its tracking mechanism is realized by first generating a mapping between virtual data id and response data, and then finding and replacing it with the actual value through virtual data id.
+
+When the original value is a reference type, the performance is the same as the original value, but the virtual data of the basic type uses `Number, String, Boolean` encapsulation classes, as well as custom `Undefined, Null` encapsulation classes, and their expressions are the same as There are some deviations from the original value. The characteristics of the virtual data and the use of auxiliary functions for the virtual data are listed below. The details of the auxiliary functions will be introduced at the end of the chapter.
+
+## string concatenation
+
+When virtual data is concatenated, it will be converted to virtual data id for splicing.
+
+```javascript
+const virtualData = createVirtualData(1);
+'a' + virtualData; // a[vd:xxxxxx]
+1 + virtualData; // 1[vd:xxxxxx]
+```
+
+## Data Comparison
+
+Virtual data cannot be directly used for comparison, but virtual data and actual data are often mixed and compared in actual scenarios. In this case, `equals` can be used for comparison.
+
+```javascript
+import { equals } from 'alova/client';
+
+equals('a', 'a'); // true
+
+const virtualData1 = createVirtualData(1);
+const virtualData2 = virtualData1.clone(); // clone virtual data
+equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same
+equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx]
+```
+
+## Participate in operations
+
+When participating in operations such as `+-*/%`, numerical comparison, and bit operations, it cannot be automatically converted to the original value. It can be converted to the original value through `dehydrateVData` and then calculated.
+
+```javascript
+import { dehydrateVData } from 'alova/client';
+
+const virtualData = createVirtualData(1);
+dehydrateVData(virtualData) + 1; // 2
+dehydrateVData(virtualData) > 0; // true
+```
+
+## type operator
+
+Because the virtual data is implemented using the encapsulation class on the basic data type, `object` will always be returned when using `typeof` to get the type, and it can also be converted to the original value by `dehydrateVData` to get the type
+
+```javascript
+const vNum = createVirtualResponse(1);
+typeof vNum === 'object'; // true
+const vUndef = createVirtualResponse(undefined);
+typeof vUndef === 'object'; // true
+
+typeof dehydrateVData(vNum) === 'number'; // true
+typeof dehydrateVData(vUndef) === 'undefined'; // true
+```
+
+To solve this problem, you can use the virtual data helper function **dehydrateVData**, which can get the original value of a virtual data, and return it unchanged when encountering non-virtual data
+
+```javascript
+const vNum = createVirtualResponse(1);
+typeof dehydrateVData(vNum) === 'number'; // true
+dehydrateVData(vNum) === 1; // true
+dehydrateVData('string') === 'string'; // true
+```
+
+## View display
+
+By default, `toString` will be called implicitly when the virtual data is displayed in the view, but sometimes you will encounter problems with display confusion, and rendering an object in **react** will report the following error:
+
+```
+Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
+```
+
+Therefore, it is recommended to use `dehydrateVData` to convert to raw data for display.
+
+## virtual data helper functions
+
+### dehydrateVData
+
+Dehydrate virtual data and return its original value, if target is non-virtual data, return it as it is.
+
+```typescript
+// type
+function dehydrateVData(target: any): any;
+
+// example
+dehydrateVData(1); // 1
+const virtualData = createVirtualData(1);
+dehydrateVData(virtualData); // 1
+```
+
+### stringifyVData
+
+Stringify virtual data, return virtual data id, when returnOriginalIfNotVData is set to false, non-virtual data will be returned as-is.
+
+```typescript
+// type
+function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any;
+
+// example
+stringifyVData(1); // 1
+stringifyVData(1, false); // undefined
+
+const virtualData = createVirtualData(1);
+stringifyVData(virtualData); // [vd:xxxxxx]
+```
+
+### equals
+
+Judge whether two values are equal in a way that is compatible with virtual data. When there is no virtual data to participate in the comparison, it will be strictly compared. Otherwise, it will be compared whether the virtual data id is the same. If there may be virtual data involved in the comparison data, it is recommended to use this function for comparison.
+
+```typescript
+// type
+function equals(prevValue: any, nextValue: any): boolean;
+
+// example
+equals('a', 'a'); // true
+const virtualData1 = createVirtualData(1);
+const virtualData2 = virtualData1.clone(); // clone virtual data
+equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same
+equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx]
+```
+
+###isVData
+
+Determine whether the target data is virtual data
+
+```typescript
+// type
+function isVData(target: any): boolean;
+
+// example
+isVData(1); // false
+const virtualData = createVirtualData(1);
+isVData(virtualData); // true
+isVData('[vd:xxxxxx]'); //true
+```
+
+## Replacement restrictions for virtual data
+
+The tracing mechanism of virtual data can only deeply traverse relevant data, and then replace the data with virtual data identifiers with actual data. If some data is generated by virtual data, it will not be recalculated after virtual data is replaced with actual data.
+
+In the following cases, even if the virtualId is replaced with actual data, the id of the request will not be recalculated. Therefore, if replacement is required, the virtualId must be directly used as a request parameter. The example is as follows:
+
+```javascript
+const deleteTodo = virtualId => {
+ return alova.Delete('/deleteTodo', {
+ id: dehydrateVData(virtualId) === null ? 1 : 2
+ });
+};
+```
+
+But if virtual data is concatenated as string, it will be automatically converted to virtual data id to participate in string concatenation, which will work. In the following cases, the value of the request id at the beginning is `id_[vd:xxxxxx]`, and when virtualId is replaced with the response value (assuming it is replaced with 1), it will be automatically updated to `id_1`.
+
+```javascript
+const deleteTodo = virtualId => {
+ return alova.Delete('/deleteTodo', {
+ id: 'id'_virtualId
+ });
+};
+```
+
+## Next
+
+Although it is simple enough to realize non-inductive interaction, there are still some additional processing compared with conservative requests. The specific implementation is roughly divided into the following steps.
+
+1. Implement functions in a conservative request manner;
+2. Manually update the list data to realize localized data compensation;
+3. Record data operations so that you can manually add to the latest records when there are unsubmitted modifications;
+4. Manually compensate the unsubmitted data to the list, so that the latest status can be displayed even if the data is not submitted;
+5. When modifying unsubmitted data, intercept requests with virtual data;
+
+Next, we will demonstrate with a simple example.
diff --git a/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md
new file mode 100644
index 000000000..d738c1967
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/03-start-silent-factory.md
@@ -0,0 +1,67 @@
+---
+title: Boot silent factory
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+The silent queue is not started by default, and we need to specify the startup parameters for initialization. In general, call `bootSilentFactory` in the entry file to initialize the silent factory, which will read unexecuted requests to the corresponding silent through the specified configuration items queues and start those queues.
+
+
+
+
+```javascript
+import { bootSilentFactory } from 'alova/client';
+import { alovaInst } from '@/api';
+
+bootSilentFactory({
+ // Specify the alova instance at startup to request information storage and request sending
+ alova: alovaInst,
+
+ // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
+ delay: 1000
+});
+```
+
+
+
+
+
+```javascript
+import { bootSilentFactory } from 'alova/client';
+import { alovaInst } from '@/api';
+
+bootSilentFactory({
+ // Specify the alova instance at startup to request information storage and request sending
+ alova: alovaInst,
+
+ // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
+ delay: 1000
+});
+```
+
+
+
+
+
+```javascript
+import { bootSilentFactory } from 'alova/client';
+import { alovaInst } from '@/api';
+
+bootSilentFactory({
+ // Specify the alova instance at startup to request information storage and request sending
+ alova: alovaInst,
+
+ // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
+ delay: 1000
+});
+```
+
+
+
+
+:::warning `delay` parameter description
+
+In actual scenarios, when entering the current page, a request is also sent to load the page data. In order to ensure that the user can see the page data faster, the request to load the data needs to be forwarded to the beginning of the queue, otherwise it may cause the loading data to fail. The request is placed at the end of the queue. At this time, it is necessary to wait until all the previous requests are completed before loading the page data. This is obviously inappropriate. Therefore, by delaying initialization for a period of time, the request for loading data enters the queue first to achieve "queue jumping" effect, the specific delay time depends on the time required for page rendering.
+
+:::
diff --git a/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md
new file mode 100644
index 000000000..fef087138
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/04-conservative-request.md
@@ -0,0 +1,135 @@
+---
+title: Step 1 - Implement features with conservative requests
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Take Todo management as an example to realize the creation, editing, deletion and other functions of Todo in the non-sense interaction mode, and the key code related to the request will be provided in the following chapters.
+
+
+
+**useSQRequest** will be used to replace **useRequest** provided by alova, and then the most common conservative request mode will be implemented first, and then the process will be done step by step Interactive mode compatibility.
+
+## Create an alova instance and related methods
+
+```javascript title="api.js"
+import { createAlova } from 'alova';
+
+export const alovaInst = createAlova({
+ /*...*/
+});
+
+/** Load todo list */
+const todoList = () => alovaInst.Get('/todo');
+
+/** Load todo details */
+const todoDetail = id =>
+ alovaInst.Get('/todo', {
+ params: { id }
+ });
+
+/** Create and edit todo items */
+const createOrEditTodo = (data, id) =>
+ alovaInst.Post('/todo', {
+ data,
+ id
+ });
+
+/** Delete the todo item */
+const deleteTodo = id => alovaInst.Delete('/todo', { id });
+```
+
+## Start the silent factory
+
+```javascript title="main.js"
+import { bootSilentFactory } from 'alova/client';
+import { alovaInst } from './api.js';
+
+bootSilentFactory({
+ alova: alovaInst
+});
+```
+
+## Load the Todo list
+
+Load and display page data in the simplest way
+
+```javascript
+import { useSQRequest } from 'alova/client';
+import { todoList } from './api.js';
+const { data, loading, error } = useSQRequest(todoList, {
+ initialData: []
+});
+```
+
+## Enter the Todo creation/editing page
+
+When creating a todo item, the id is empty, and no request for obtaining details is sent. When editing a todo item, if the id has a value, the detailed data will be obtained.
+
+```javascript
+import { useSQRequest } from 'alova/client';
+import { todoDetail } from './api.js';
+
+const id = /* todo id */;
+const { loading, data } = useSQRequest(() => todoDetail(id), {
+ initialData: {
+ title: '',
+ time: new Date()
+ },
+ immediate: !!id
+});
+```
+
+## Create/Edit Todo Items
+
+By submitting an event trigger request, after the submission is successful, call fetch to re-fetch the latest list data, and the interface will automatically display the latest data.
+
+```javascript
+import { useFetcher } from 'alova';
+import { useSQRequest } from 'alova/client';
+import { createOrEditTodo, todoList } from './api.js';
+
+const id = /* todo id */;
+const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, {
+ immediate: false,
+});
+
+const { fetch } = useFetcher();
+onSuccess(() => {
+ // Re-fetch list data
+ fetch(todoList);
+})
+
+// submit event callback function
+const handleSubmit = newData => {
+ send(newData, id);
+};
+
+```
+
+## Delete Todo item
+
+Delete the corresponding todo item by id
+
+```javascript
+import { useSQRequest } from 'alova/client';
+import { deleteTodo, todoList } from './api.js';
+
+const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, {
+ immediate: false
+});
+
+const { fetch } = useFetcher();
+onSuccess(() => {
+ // Re-fetch list data
+ fetch(todoList);
+});
+
+// Event callback triggers delete request
+const handleDelete = deletingId => {
+ send(deletingId);
+};
+```
+
+So far, a simple Todo list management related request function has been completed, and then we will start to transform it to be compatible with the non-sense interaction mode.
diff --git a/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md
new file mode 100644
index 000000000..083d03cb9
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/05-modify-response.md
@@ -0,0 +1,259 @@
+---
+title: Step 2 - Adjust Response Handling
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In the conservative request example in the previous section, we called `fetch` to re-fetch the refreshed page after creating, editing, and deleting requests for Todo items. In order to display the results immediately after the operation, we need to make the following adjustments:
+
+1. Set the behavior mode of create, edit and delete requests to `silent`, they will trigger the success callback immediately when the request is made;
+2. Manually update the list, instead of pulling data, use virtual data to occupy the response data of the server;
+3. Save operation records for data compensation when refreshing the page;
+
+## Set behavior mode
+
+Set by configuring the parameter `behavior`, the optional parameters are `queue`, `silent`, `static`, or a function that returns behavior data to dynamically set the behavior mode, the default is `queue`.
+
+The following sets the behavior parameters statically.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ // highlight-start
+ behavior: 'silent',
+ // highlight-end
+ immediate: false
+});
+```
+
+The following is to dynamically set the behavior parameter.
+
+```javascript
+const { send } = useSQRequest(createOrEditTodo, {
+ // highlight-start
+ // The arg parameter can be passed in through the send function
+ behavior: arg => {
+ if (arg === 0) return 'silent';
+ return 'queue';
+ },
+ // highlight-end
+ immediate: false
+});
+```
+
+> When behavior is set as a function, it will be called every time a request is initiated to determine which behavior to process this request.
+
+## Silent queue description
+
+After setting the behavior parameter to `queue` or `silent`, the request will enter the silent queue and wait for the request to be initiated. By default, they will enter the queue named `default`. You can also specify other queues to save silentMethod instances. without interfering with each other.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ // highlight-start
+ // The specified request information enters the queue named queue-2
+ queue: 'queue-2',
+ // highlight-end
+ behavior: 'silent',
+ immediate: false
+});
+```
+
+## Manually update the list in the callback
+
+### Update the list after adding/editing
+
+
+
+
+When the list page is not destroyed, such as using the modal box operation on the current page, or using `` (Vue) to keep the page components, the data will still exist. At this time, we use **updateStateEffect** to Update the list data. Compared with the **updateState** exported by alova, it has the function of tracking virtual data. When the response data is obtained, it will automatically track the virtual data in the list data and replace it with the actual data.
+
+```javascript
+import { useSQRequest, updateStateEffect } from 'alova/client';
+import { createOrEditTodo, todoList } from './api.js';
+
+const { onSuccess } = useSQRequest(createOrEditTodo, {
+ behavior: 'silent',
+ immediate: false,
+
+ // highlight-start
+ // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data
+ // For example, when creating a Todo item, the id of this piece of data will be returned.
+ silentDefaultResponse: () => {
+ return {
+ id: '--'
+ };
+ }
+ // highlight-end
+});
+
+// highlight-start
+onSuccess(({ data, silentMethod }) => {
+ // Construct list data items
+ const editingItem = {
+ ...detail,
+
+ // When editing, use the original id, otherwise use the id in the response data
+ // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value
+ id: id || data.id
+ };
+
+ // use updateStateEffect instead of updateState
+ updateStateEffect(todoList(), todoListRaw => {
+ if (id) {
+ todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item));
+ } else {
+ todoListRaw.unshift(editingItem);
+ }
+ return todoListRaw;
+ });
+});
+// highlight-end
+```
+
+> updateStateEffect is used in the same way as [updateState](/next/tutorial/client/in-depth/update-across-components)
+
+
+
+
+When the list page has been destroyed and the data has been released, such as jumping to a new page, use **setCache** to update the cache data. When the list page is returned, the request will be re-initiated and the updated cache will be hit.
+
+```javascript
+import { useSQRequest, setCache, equals } from 'alova/client';
+import { createOrEditTodo, todoList } from './api.js';
+
+const urlParams = new URLSearchParams(window.location.search);
+const id = urlParams.get('id') || '';
+const { onSuccess } = useSQRequest(createOrEditTodo, {
+ behavior: 'silent',
+ immediate: false,
+
+ // highlight-start
+ // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data
+ // For example, when creating a Todo item, the id of this piece of data will be returned.
+ silentDefaultResponse: () => {
+ return {
+ id: '--'
+ };
+ }
+ // highlight-end
+});
+// highlight-start
+onSuccess(({ data, silentMethod }) => {
+ // Construct list data items
+ const editingItem = {
+ ...detail,
+
+ // When editing, use the original id, otherwise use the id in the response data
+ // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value
+ id: id || data.id
+ };
+
+ const method TodoList = todoList();
+ setCache(methodTodoList, todoListRaw => {
+ if (id) {
+ todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item));
+ } else {
+ todoListRaw.unshift(editingItem);
+ }
+ return todoListRaw;
+ });
+ // Call setUpdateState to set response data tracking, so as to achieve the same delayed update effect as updateStateEffect
+ if (silentMethod) {
+ silentMethod.setUpdateState(methodTodoList);
+ silentMethod.save();
+ }
+});
+// highlight-end
+```
+
+
+
+
+### Update list after removal
+
+```javascript
+import { useSQRequest, updateStateEffect } from 'alova/client';
+import { deleteTodo, todoList } from './api.js';
+
+const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, {
+ immediate: false,
+ // highlight-start
+ behavior: 'silent'
+ // highlight-end
+});
+
+onSuccess(({ sendArgs: [deletingId] }) => {
+ updateStateEffect(todoList(), todoListRaw =>
+ todoListRaw.filter(item => item.id !== deletingId)
+ );
+});
+
+// Event callback triggers delete request
+const handleDelete = deletingId => {
+ send(deletingId);
+};
+```
+
+## Save the operation record
+
+It is not enough to just update the list manually. We also need to consider that when the network is restored and there are still waiting requests in the request queue, the list data loaded at this time does not include the part of the unsubmitted request, which will cause certain problems for the user. Puzzled:
+
+> "I have clearly added multiple pieces of data, why is it not in the list?"
+
+Therefore, we need to record the operation and related data in the success callback, so that when the list data is loaded again, the uncommitted data will be manually compensated to the list, so that the list data will always be kept up-to-date.
+
+Saving operation records is also very simple, you only need to mount the relevant data to the silentMethod instance, and it will be persisted along with the instance.
+
+### create/edit success callback
+
+```javascript
+//...
+onSuccess(({ silentMethod }) => {
+ // Construct list data items
+ const editingItem = {
+ ...detail,
+ id: id || data.id
+ };
+ //...
+ // highlight-start
+ if (silentMethod) {
+ // Set the name for subsequent queries
+ // If editingItem.id is virtual data will be automatically converted to its id
+ silentMethod.entity.setName('edit' + editingItem.id);
+ silentMethod.reviewData = {
+ operate: id ? 'edit' : 'add',
+ data: editingItem
+ };
+ silentMethod.save();
+ }
+ // highlight-end
+});
+```
+
+### delete success callback
+
+```javascript
+//...
+onSuccess(({ sendArgs: [deletingId], silentMethod }) => {
+ //...
+ // highlight-start
+ if (silentMethod) {
+ silentMethod.reviewData = {
+ operate: 'delete',
+ data: {
+ id: deletingId
+ }
+ };
+ silentMethod.save();
+ }
+ // highlight-end
+});
+```
+
+### Precautions
+
+1. In the onSuccess callback function, silentMethod has a value only in the `queue` and `silent` behavior modes;
+2. Generally speaking, you can use `silentMethod.a = ...` or `silentMethod.b = ...` to save operation records, but it will report an error in typescript, so _reviewData_ is specially provided as a silent Submit the save attribute of the operation record;
+3. After modifying the silentMethod data, you need to save the modification through `silentMethod.save()`;
+
+The next step is to set retry parameters on silent submit requests.
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md
similarity index 96%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md
rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md
index 1b8ef082b..b9685ed3a 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/06-request-retry.md
@@ -1,144 +1,143 @@
----
-title: Step 3 - Set Request Retry
-sidebar_position: 60
----
-
-When a request enters the silent queue, you can set request retry parameters for it to ensure its request success rate. This is valid when the behavior mode is set to **queue** and **silent**. The difference is, The request under the behavior of **silent** is persistent by default, and the request will continue to be sent even if it is refreshed before the request succeeds, while the request under the behavior of **queue** will not be persisted and will be cleared after refreshing.
-
-## Maximum number of retries
-
-Set the maximum number of retries, no retries by default.
-
-```javascript
-useSQRequest(createOrEditTodo, {
- //...
- // highlight-start
- // The number of retries is 3 times
- maxRetryTimes: 3
- // highlight-end
-});
-```
-
-## Request delay time
-
-By default, each retry interval is 1000ms, and we can customize the delay time of each retry in the avoidance strategy.
-
-```javascript
-useSQRequest(createOrEditTodo, {
- //...
- maxRetryTimes: 3,
- // highlight-start
- // Requests are delayed by 2000ms each time
- backoff: {
- delay: 2000
- }
- // highlight-end
-});
-```
-
-If you need to increase the delay time according to the rules, you can set a growth factor for it.
-
-```javascript
-useSQRequest(createOrEditTodo, {
- //...
- maxRetryTimes: 3,
- backoff: {
- delay: 2000,
- // highlight-start
- // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, and the third is 6 seconds
- multiplier: 2
- // highlight-end
- }
-});
-```
-
-not enough? You can even add a random jitter value to each delay to make it look less regular
-
-```javascript
-useSQRequest(createOrEditTodo, {
- //...
- maxRetryTimes: 3,
- backoff: {
- delay: 2000,
- multiplier: 2,
- // highlight-start
- /**
- * The initial jitter percentage value of the delay request, the range is 0-1
- * When only startQuiver is set, endQuiver defaults to 1
- * For example set to 0.5, it will add 50% to 100% random time on the current delay time
- * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver
- */
- startQuiver: 0.5,
-
- /**
- * The jitter end percentage value of the delayed request, the range is 0-1
- * When only endQuiver is set, startQuiver defaults to 0
- * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time
- * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver
- */
- endQuiver: 0.8;
- // highlight-end
- }
-});
-```
-
-## set retry rules
-
-By default, as long as the request fails, it will be retried. The request failure is divided into the following situations:
-
-1. The request is wrong, and the error is not caught by the global `onError` hook;
-2. The request was successful, but an error was thrown in the global `onSuccess` hook;
-
-But in reality, not all requests need to be retried. For example, when a server error occurs or the network is disconnected, it should not be retried. In this case, it is necessary to set a retry judgment rule. When a request fails, an instance of `Error` is usually obtained. We can set a regular expression to match `error.message` or `error.name`, and if the match passes, no retry will be made.
-
-```javascript
-useSQRequest(createOrEditTodo, {
- //...
- // highlight-start
- // When the thrown error name is 500, or the wrong message matches network error, do not retry
- retryError: {
- name: /^500$/,
- message: /network error/i
- }
- // highlight-end
-});
-```
-
-You can also set one of the matching rules. When only setting the matching rules for `message`, it can be directly abbreviated as a regular expression.
-
-```javascript
-// Only set the name that matches the error
-useSQRequest(createOrEditTodo, {
- //...
- retryError: {
- name: /^500$/
- }
-});
-
-// Only set the message that matches the error
-useSQRequest(createOrEditTodo, {
- //...
- retryError: /network error/i
-});
-```
-
-In order not to pollute the error message, usually we will put the error code returned by the server in `error.name`, of course, you can also splice it into `error.message`, the error handling example of Response is as follows:
-
-```javascript
-const alovaInst = createAlova({
- //...
- responded: {
- onSuccess(response) {
- // Error thrown on 500 error
- if (response.status === 500) {
- const error = new Error(response.statusText);
- error.name = response.status;
- throw error;
- }
- return response.json();
- }
- }
-});
-```
-
-In the next step, the saved operation records will be used to perform data compensation on the list data to achieve the latest state.
+---
+title: Step 3 - Set Request Retry
+---
+
+When a request enters the silent queue, you can set request retry parameters for it to ensure its request success rate. This is valid when the behavior mode is set to **queue** and **silent**. The difference is, The request under the behavior of **silent** is persistent by default, and the request will continue to be sent even if it is refreshed before the request succeeds, while the request under the behavior of **queue** will not be persisted and will be cleared after refreshing.
+
+## Maximum number of retries
+
+Set the maximum number of retries, no retries by default.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ // highlight-start
+ // The number of retries is 3 times
+ maxRetryTimes: 3
+ // highlight-end
+});
+```
+
+## Request delay time
+
+By default, each retry interval is 1000ms, and we can customize the delay time of each retry in the avoidance strategy.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ maxRetryTimes: 3,
+ // highlight-start
+ // Requests are delayed by 2000ms each time
+ backoff: {
+ delay: 2000
+ }
+ // highlight-end
+});
+```
+
+If you need to increase the delay time according to the rules, you can set a growth factor for it.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ maxRetryTimes: 3,
+ backoff: {
+ delay: 2000,
+ // highlight-start
+ // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, and the third is 6 seconds
+ multiplier: 2
+ // highlight-end
+ }
+});
+```
+
+not enough? You can even add a random jitter value to each delay to make it look less regular
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ maxRetryTimes: 3,
+ backoff: {
+ delay: 2000,
+ multiplier: 2,
+ // highlight-start
+ /**
+ * The initial jitter percentage value of the delay request, the range is 0-1
+ * When only startQuiver is set, endQuiver defaults to 1
+ * For example set to 0.5, it will add 50% to 100% random time on the current delay time
+ * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver
+ */
+ startQuiver: 0.5,
+
+ /**
+ * The jitter end percentage value of the delayed request, the range is 0-1
+ * When only endQuiver is set, startQuiver defaults to 0
+ * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time
+ * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver
+ */
+ endQuiver: 0.8;
+ // highlight-end
+ }
+});
+```
+
+## set retry rules
+
+By default, as long as the request fails, it will be retried. The request failure is divided into the following situations:
+
+1. The request is wrong, and the error is not caught by the global `onError` hook;
+2. The request was successful, but an error was thrown in the global `onSuccess` hook;
+
+But in reality, not all requests need to be retried. For example, when a server error occurs or the network is disconnected, it should not be retried. In this case, it is necessary to set a retry judgment rule. When a request fails, an instance of `Error` is usually obtained. We can set a regular expression to match `error.message` or `error.name`, and if the match passes, no retry will be made.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ // highlight-start
+ // When the thrown error name is 500, or the wrong message matches network error, do not retry
+ retryError: {
+ name: /^500$/,
+ message: /network error/i
+ }
+ // highlight-end
+});
+```
+
+You can also set one of the matching rules. When only setting the matching rules for `message`, it can be directly abbreviated as a regular expression.
+
+```javascript
+// Only set the name that matches the error
+useSQRequest(createOrEditTodo, {
+ //...
+ retryError: {
+ name: /^500$/
+ }
+});
+
+// Only set the message that matches the error
+useSQRequest(createOrEditTodo, {
+ //...
+ retryError: /network error/i
+});
+```
+
+In order not to pollute the error message, usually we will put the error code returned by the server in `error.name`, of course, you can also splice it into `error.message`, the error handling example of Response is as follows:
+
+```javascript
+const alovaInst = createAlova({
+ //...
+ responded: {
+ onSuccess(response) {
+ // Error thrown on 500 error
+ if (response.status === 500) {
+ const error = new Error(response.statusText);
+ error.name = response.status;
+ throw error;
+ }
+ return response.json();
+ }
+ }
+});
+```
+
+In the next step, the saved operation records will be used to perform data compensation on the list data to achieve the latest state.
diff --git a/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md
new file mode 100644
index 000000000..b35c45799
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/07-data-compensation.md
@@ -0,0 +1,55 @@
+---
+title: Step 4 - Data Compensation
+---
+
+The user may perform some data operations in a disconnected environment. At this time, the silent queue will be full of unsubmitted requests. When the network is restored, due to the limitation of the timing mechanism, it will take a little time to complete these requests. The list loaded at this time The data does not include unsubmitted requests, which can cause some confusion for users:
+
+> "I have clearly added multiple pieces of data, why is it not in the list?"
+
+Therefore, we need to manually compensate the unsubmitted data to the list, so that the list data is always kept up-to-date. In this step, the saved operation records will be used to compensate the list data. It is actually very simple. We It is only necessary to traverse the silentMethod instance of the relevant queue after the list request is successful, and update the operation records recorded in the previous step to the list data.
+
+```javascript
+import { useSQRequest, filterSilentMethods, equals } from 'alova/client';
+import { todoList } from './api.js';
+const { data, loading, onSuccess } = useSQRequest(todoList, {
+ initialData: []
+});
+
+onSuccess(() => {
+ // Get all silentMethod instances under the default queue
+ const silentMethods = filterSilentMethods();
+ silentMethods.forEach(({ reviewData }) => {
+ if (!reviewData) {
+ return;
+ }
+ const { operate, data } = reviewData;
+ const index = todoListData.findIndex(({ id }) => equals(id, data.id));
+ if ((operate === 'edit' || operate === 'remove') && index >= 0) {
+ operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1);
+ } else if (operate === 'add' && index < 0) {
+ // There will be added uncommitted items when re-requesting and hitting the cache, these need to be filtered
+ todoListData.unshift(data);
+ }
+ });
+});
+```
+
+## Explanation of related functions
+
+### filterSilentMethods
+
+Filter out the specified silentMethod instance by method name or regular expression, which is defined as follows:
+
+```typescript
+function filterSilentMethods(
+ methodNameMatcher?: string | number | RegExp,
+ queueName?: string,
+ filterActive?: boolean
+): SilentMethod[];
+```
+
+**methodNameMatcher: **method name matcher, if it is a number or string, it will filter out the results that completely match the name, if it is a regular expression, it will filter out the matching results, if it is not passed, it will filter out all the results;
+
+**queueName**: Specify the queue to search for, if not uploaded, the _default_ queue will be searched by default;
+
+**filterActive**: whether to filter out the active state instance
diff --git a/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md
new file mode 100644
index 000000000..1f3c3286f
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/08-edit-item.md
@@ -0,0 +1,110 @@
+---
+title: Step 5 - Edit Data
+---
+
+> What should I do when the user needs to edit data when the network is disconnected?
+
+At this point, two situations need to be explained:
+
+1. The list data can meet the data echo of the edit page. At this time, the list data can be passed to the edit page without requesting. At this time, all list data supports editing in the silent submission mode;
+2. The echo data on the edit page needs to be obtained through the api, and only the locally cached list items can echo the data normally, for example:
+ 1. For the list items that have been accessed before the network is disconnected, the request can hit the cache again;
+ 2. Created through the silent submission mode, but the list item has not been successfully submitted, and the submitted data still exists in the silentMethod instance;
+
+And here we will focus on the case of **2-2**.
+
+## Edit silent submit items
+
+In the previous chapters, we know that when the newly created data item has not been successfully submitted, the virtual data will be used as the placeholder for the id. Usually, we also get the data item through the id. At this time, we are in `useSQRequeset` Virtual data interception is implemented on the above, if a request is accompanied by virtual data information, it will be intercepted before sending and the data can be specified to replace the response data, and the request will be abandoned.
+
+Remember the **silentMethod.reviewData** saved in [Step 2 - Adjust Response Handling](/next/tutorial/client/strategy/sensorless-data-interaction/modify-response)?
+
+```javascript
+onSuccess(({ silentMethod }) => {
+ // Construct list data items
+ const editingItem = {
+ ...detail,
+ id: id || data.id
+ };
+ //...
+ if (silentMethod) {
+ // highlight-start
+ // Setting the name is to find the corresponding silentMethod instance when intercepting
+ silentMethod.entity.setName('edit' + editingItem.id);
+ silentMethod.reviewData = {
+ operate: id ? 'edit' : 'add',
+ data: editingItem
+ };
+ // Don't forget to call save
+ silentMethod.save();
+ // highlight-end
+ }
+});
+```
+
+It can be used not only for data compensation, but also for echoing data in edit pages.
+
+```javascript
+const { loading, data } = useSQRequest(id => todoDetail(id), {
+ initialData: {
+ title: '',
+ time: new Date()
+ },
+ immediate: false,
+
+ // highlight-start
+ // Set the interception function, the function will be called when there is virtual data in this request
+ // If reviewData is returned, it will replace the response data and give up this request, otherwise the request will still be initiated
+ vDataCaptured: () => {
+ const targetSM = filterSilentMethods('edit' + todoId).pop();
+ if (targetSM?.reviewData) {
+ return { ...targetSM.reviewData.data };
+ }
+ }
+ // highlight-end
+});
+```
+
+:::warning Caution
+
+You can save enough data in **silentMethod.reviewData** to satisfy both list data compensation and edit page data echo.
+
+:::
+
+So far, data items created through silent submit mode also support editing! What's the problem, um... and one last one.
+
+## When the data item being edited is submitted successfully
+
+When the user is editing a data item that has not been successfully submitted, it suddenly submits successfully! At this time, we need to replace the virtual data used in the edit page with actual data, for example, replace the virtual id with the actual id, and use the actual id to submit in the next edit. This is also very simple, we only need to monitor This is done by silently submitting the success event, which will receive a data collection consisting of virtual data and real data.
+
+```javascript
+import { onSilentSubmitSuccess, stringifyVData } from 'alova/client';
+
+//...
+// id is virtual data during initialization
+let id = /* todo virtual id */;
+
+// highlight-start
+// Binding listener silently submits the successful event to update the id, and returns the unbind function, don't forget to call the unbind function when the component is destroyed
+const unbindEvent = onSilentSubmitSuccess(event => {
+ const vDataId = stringifyVData(id);
+ if (event.vDataResponse[vDataId]) {
+ id = event.vDataResponse[vDataId];
+
+ // The following is to change the virtual id in the url to the actual id
+ history.replaceState(null, '', '?id=' + currentId);
+ }
+});
+// highlight-end
+```
+
+Here, the `event.vDataResponse` value is a collection of virtual data id and actual data, and its format is as follows:
+
+```javascript
+{
+ '[vd:aaaaaa]': { id: 1 },
+ '[vd:bbbbbb]': 1
+}
+```
+
+So far, we have completed all the content of a simple list of non-inductive interaction, but in other application scenarios such as editing applications, complex list management, etc., we may encounter more different needs. What else does alova have at this time? What are the features we can use? Please read the next chapter!
diff --git a/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md
new file mode 100644
index 000000000..cf14e0282
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/09-what-more.md
@@ -0,0 +1,352 @@
+---
+title: What more?
+---
+
+## Description of the role of virtual data
+
+In the previous chapters we used virtual data as the id placeholder, but its function is more than that, it can occupy any response data, for example, in a complex list, when creating a data item, the server needs to calculate additional Data, at this time, these additional data can also be occupied by virtual data, but this requires that the additional data needs to be returned together when the data item is created. See the following example:
+
+```javascript
+const { onSuccess, send } = useSQRequest(createOrEditData, {
+ behavior: 'silent',
+ immediate: false,
+
+ // Construct the same data structure as the response data
+ silentDefaultResponse: () => {
+ return {
+ id: '--',
+ extra1: '',
+ extra2: ''
+ };
+ }
+});
+onSuccess(event => {
+ event.data.id; // virtual data
+ event.data.extra1; //virtual data
+ event.data.extra2; //virtual data
+});
+```
+
+## New events in useSQRequest
+
+In order to better monitor the behavior of requests in the queue, `useSQRequset` also provides the following 3 additional event monitoring functions, you can obtain the binding functions in the following ways.
+
+```javascript
+const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */);
+```
+
+###onBeforePushQueue
+
+silentMethod is an event before entering the request queue. It is valid when the behavior mode is `queue` or `silent`. You can return `false` in this event callback to prevent the current silentMethod from entering the queue. For example, you may want to replace the current silentMethod with another one. It can be done like this:
+
+```javascript
+//...
+onBeforePushQueue(event => {
+ // Replace the old silentMethod with the specified id each time, reducing the number of requests
+ const prevSumbmitMethod = getSilentMethod('temp' + id);
+ if (event.silentMethod && prevSumbmitMethod) {
+ prevSumbmitMethod.replace(event.silentMethod);
+ return false;
+ }
+});
+```
+
+###onPushedQueue
+
+silentMethod The event after entering the queue. It is valid when the behavior mode is `queue` or `silent`. If the queue is blocked in the **onBeforePushQueue** event, this function will not trigger.
+
+### onFallback
+
+Similar to the traditional optimistic ui solution, we also provide a request rollback event, which will be triggered when the request reaches the maximum number of retries or the retry judgment fails. You can use it to handle some rollback operations.
+
+:::warning Warning
+
+When the fallback event is bound, even if the behavior mode is `silent`, the request will no longer be persisted, and it will be lost after refreshing the page. This is because the persistent silentMethod usually needs to ensure completion, not Rollbacks allow the user to re-process, in which case the rollback function should not be used.
+
+:::
+
+## Save additional operation data
+
+When creating or editing a data item, the previous chapters only saved the echo data to `silentMethod.reviewData`, if there are some additional data that need to be recorded, such as the menu options of the edit page, etc., we also need to record them to ensure They can also be selected when the network is disconnected. At this time, these data are mounted on the silentMethod instance and persisted together.
+
+Generally speaking, you can save persistent data with any property name, but an error will be reported in typescript, so the `silentMethod.extraData` attribute is specified for you as the storage field for extra data, remember to use `silentMethod.save()` for persistence data.
+
+## Custom serializer
+
+By default, alova uses localStorage for silentMethod data persistence, so it will call `JSON.stringify` to convert to a string when persisting, but json data only supports basic data types, pure objects and arrays, if you want Serialize special data structures such as Date instances, RegExp instances, functions, and custom class instances. Alova supports custom serializers to handle them. How to convert it to a data structure supported by json when storing it? How to convert to the original object structure.
+
+```javascript
+const regExpSerializer = {
+ // forward is called when serializing
+ // Need to judge whether the data is the target value, if not, return undefined, indicating that it will not be processed
+ forward: data => data instanceof RegExp ? data.source : undefined,
+
+ // backward is called during deserialization, data is the value returned in forward
+ backward: source => new RegExp(source);
+};
+
+bootSilentFactory({
+ //...
+ // use this serializer via serializers
+ serializers: {
+ customRegExp: regExpSerializer
+ }
+})
+```
+
+SilentFactory provides Date and RegExp serializers by default, and you can also use the same key to override the default serializers
+
+```javascript
+const defaultSerializers = {
+ Date: dateSerializer,
+ RegExp: regExpSerializer
+};
+```
+
+[Read Date serializer source code](https://github.com/alovajs/alova/blob/main/packages/client/src/util/serializer/date.ts)
+
+## Manipulate the silent queue
+
+Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains relevant configuration for silent submission. Any number of silent queues can be generated, and it supports searching, modifying, and deleting silentMethod instances in the queue.
+
+### Using multiple silent queues
+
+Requests whose behavior mode is set to `queue` and `silent` will enter the silent queue. By default, the silentMethod instance will be assigned to the **default** queue. When it needs to be assigned to other queues, it can be assigned in _useSQRequest_ Specify the `queue` parameter.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ // Specify the silentMethod instance to enter the queue named customQueue
+ queue: 'customQueue',
+ behavior: 'silent'
+});
+```
+
+You can also specify `queue` as a function to return the name of the queue. This function will be called every time a request is made, and the function parameters come from the send function.
+
+```javascript
+const { send } = useSQRequest(createOrEditTodo, {
+ //...
+ // Determine whether to enter the customQueue queue according to useCustomQueue
+ queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'),
+ behavior: 'silent',
+ immediate: false
+});
+const handleClick = () => {
+ send(true);
+};
+```
+
+### Find silentMethod
+
+In the previous [data compensation](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation), we used [filterSilentMethods](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) to find the silentMethod of the specified queue instance, it will return all matching silentMethod instances, here are two more ways to find the queue:
+
+#### Find a silentMethod instance
+
+Use `getSilentMethod` to query the first matching silentMethod instance, the usage is the same as [filterSilentMethods](/next/tutorial/client/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods).
+
+```typescript
+function filterSilentMethods(
+ methodNameMatcher?: string | number | RegExp,
+ queueName?: string,
+ filterActive?: boolean
+): SilentMethod | undefined;
+```
+
+#### Custom Lookup
+
+Customize the lookup through the exported `silentQueueMap` queue collection, the data structure of `silentQueueMap` is:
+
+```javascript
+const silentQueueMap = {
+ default: [silentMethod1, silentMethod2 /* ... */],
+ queueName1: [silentMethod3, silentMethod4 /* ... */],
+ queueName2: [silentMethod5, silentMethod6 /* ... */]
+ //...
+};
+```
+
+### Change the silentMethod in the queue
+
+After finding the silentMethod instance you want, you can manipulate these waiting silentMethod instances.
+
+#### update silentMethod
+
+Call `silentMethod.save` to update this silentMethod data to cache.
+
+```javascript
+silentMethod.extraData = { ... };
+await silentMethod.save();
+```
+
+#### replace silentMethod
+
+Call `silentMethod.replace` to replace a silentMethod with another silentMethod in the queue.
+
+```javascript
+oldSilentMethod.replace(newSilentMethod);
+```
+
+#### remove silentMethod
+
+Call `silentMethod.remove` to remove the current silentMethod from the queue.
+
+```javascript
+oldSilentMethod.remove();
+```
+
+#### Use silentQueueMap to change silentMethod
+
+You can also access `silentQueueMap` to custom change any data of any queue.
+
+```javascript
+import { silentQueueMap } from 'alova/client';
+
+// Modify all silentMethods in the default queue
+silentQueueMap.default.forEach(silentMethodItem => {
+ //...
+});
+```
+
+## Queue request delay
+
+Some applications need to submit data frequently, such as editor-type applications, which are saved in real time during the editing process without aborting the user's use. When using silent submission in this type of application, more request information will be generated, not only It will fill up the front-end cache and make the server receive too many requests. At this time, we may no longer need to synchronize all save operations, but send an operation within a period of time. There will be the following two solutions:
+
+1. Throttle the editing operation, and only initiate one submission within n seconds. This solution may lose the operation records during the delay period, resulting in only the status of the last submission being obtained when refreshing;
+2. Delay the save request in the queue, and only keep the latest request information during the delay time, so that you can reduce the request while retaining the latest editing status;
+
+By default, the silentMethod in the queue will send the request immediately after the last response. We can set the delay sending time of the silentMethod through `requestWait` at startup, and keep the latest silentMethod by manipulating the queue during this time.
+
+### Set request delay
+
+You can set the delay time for the specified queue, or you can set multiple queues at once.
+
+```javascript
+bootSilentFactory({
+ alova: alovaInst,
+ // highlight-start
+ requestWait: [
+ // Each silentMethod in the customQueue queue will delay 5000ms to initiate a request
+ { queue: 'customQueue', wait: 5000 },
+
+ //Use a regular expression to uniformly set a 3000ms delay request time for the queue whose name is prefixed with delay
+ { queue: /^delay/, wait: 3000 }
+ ]
+ // highlight-end
+});
+```
+
+When `requestWait` is directly set to a number, it is valid for the default queue by default.
+
+```javascript
+bootSilentFactory({
+ alova: alovaInst,
+ // highlight-start
+ // Each silentMethod in the default queue will delay 5000ms to initiate a request
+ requestWait: 5000
+ // highlight-end
+});
+```
+
+### Dynamically set the request delay
+
+Many times we only want a specific silentMethod in the queue to set a delay request. At this time, a function can be used to dynamically set the request delay. Each silentMethod of the specified queue will call this function before initiating a request to determine the delay time.
+
+```javascript
+bootSilentFactory({
+ alova: alovaInst,
+ // highlight-start
+ requestWait: [
+ {
+ queue: /^delay,
+ // Only delay 5000ms for post requests with url /edit
+ wait: (silentMethod, queueName) => {
+ const { type, url, data } = silentMethod.entity;
+ if (type === 'POST' && url === '/edit') {
+ return 5000;
+ }
+ }
+ },
+ ]
+ // highlight-end
+});
+```
+
+Similarly, when `requestWait` is set directly to a function, it defaults to the default queue.
+
+```javascript
+bootSilentFactory({
+ alova: alovaInst,
+ // highlight-start
+ requestWait: (silentMethod, queueName) => {
+ const { type, url, data } = silentMethod.entity;
+ if (type === 'POST' && url === '/edit') {
+ return 5000;
+ }
+ }
+ // highlight-end
+});
+```
+
+## Dynamically set the method name
+
+For easy search, if you need to dynamically set the name of the method in silentMethod, you can call setName.
+
+```javascript
+// Reset the name of silentMethod on successful request
+onSuccess(({ data, silentMethod }) => {
+ silentMethod.entity.setName('name' + data.id);
+});
+```
+
+## Global silent submit event
+
+In the previous chapter, we touched `onSilentSubmitSuccess`, we provided a total of 5 global events.
+
+### onSilentSubmitBoot
+
+Silent factory start event, triggered after the silent factory is started.
+
+```typescript
+function onSilentSubmitBoot(handler: () => void): OffEventCallback;
+```
+
+###onBeforeSilentSubmit
+
+Fired before a silentMethod request with `behavior=silent`.
+
+```typescript
+function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback;
+```
+
+###onSilentSubmitSuccess
+
+Fired when a silentMethod request with `behavior=silent` succeeds.
+
+```typescript
+function onSilentSubmitSuccess(
+ handler: (event: GlobalSQSuccessEvent) => void
+): OffEventCallback;
+```
+
+### onSilentSubmitError
+
+Fired when the silentMethod request with `behavior=silent` fails, but the maximum number of retries has not been reached.
+
+```typescript
+function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback;
+```
+
+### onSilentSubmitFail
+
+When the silentMethod of `behavior=silent` encounters a request failure, the following 3 situations will trigger this event:
+
+1. Triggered when the request reaches the maximum number of retries;
+2. When the number of retries is not set, the first request failure will trigger;
+3. Triggered when the request has not reached the maximum number of retries, but the retry is judged to be no more retries;
+
+```typescript
+function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback;
+```
+
+The above event binding functions will return the unbinding function, and you can unbind the event before the component is unmounted.
diff --git a/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md
new file mode 100644
index 000000000..f04e2c73a
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/README.md
@@ -0,0 +1,146 @@
+---
+title: Sensorless data interaction - Overview
+---
+
+Non-inductive data interaction means that when users interact with the application, relevant content can be displayed immediately without waiting, or the operation result can be displayed without waiting when submitting information, just like interacting with local data, thereby greatly improving the smoothness of the application It allows users to not feel the lag caused by data transmission.
+
+This is not new. The concept of optimistic update existed before 2015. It refers to displaying the submission results to the interface before the server responds. It is based on the assumption that most submissions are successful. The opposite is a conservative update, that is, the server will display a wait state before responding until the request is completed. In terms of handling failures, the current optimistic update solution is usually handled through fallback, such as the following sample code:
+
+```javascript
+const list = [];
+const data = {};
+addTodo(data).catch(() => {
+ list = list.filter(item => item !== data);
+});
+list.push(data);
+```
+
+This can cause the following problems:
+
+1. Rollback will increase the user's understanding and operation costs;
+2. Request timing issues;
+3. If subsequent requests depend on this submission, this failure will make subsequent requests meaningless;
+4. Possible lost requests;
+
+After several months of program design and continuous iteration, alova has taken a big step in this area. In our program, the above problems have been solved, which can ensure the success of the request more stably. Although there are still technical limitations, But it has been applied in many scenarios. In our technical solution, the problems caused by network fluctuations can be reduced to a higher extent. Your application is still available in high-latency networks or even disconnected, and the latest data can still be maintained after refreshing the page.
+
+## Application scenarios
+
+Although non-inductive data interaction cannot be used on a large scale, it is very suitable in certain scenarios. During the exploration, we found at least including but not limited to the following scenarios for your reference.
+
+### Editor application
+
+Note-taking applications such as Evernote and canvas editing applications such as MasterGO need to meet the following requirements:
+
+1. When entering the note or drawing list, the list data will be pulled in full, and the local cache data will be used next time;
+2. Real-time synchronization to the server during the editing process, and the synchronization process occurs in the background, which will not affect the normal use of users;
+3. You can continue to use it even when the network is poor or disconnected;
+
+
+
+### Setup module
+
+The setting module composed of commonly used switches and selectors needs to realize the requirement that the user operation is synchronized to the server in real time, and the submission status is no longer displayed, but the latest status after the operation is directly displayed.
+
+
+
+### Simple list management
+
+The data we fill in when creating a list item is enough for the display of the list page, which is called a simple list. For example, a student list page displays the three data of the student's name, gender, and grade. These three data are required when creating a student fill in. In a simple list the following requirements will be fulfilled:
+
+1. Immediately display the latest status on the list page when adding, editing and deleting list items, no need to display it after the submission is completed, and it is not limited by network fluctuations;
+2. When the page is refreshed, the list page is always kept up to date;
+
+
+
+### Complex list management
+
+A complex list means that the data filled in when creating a list item is not enough for display on the list page, and additional data needs to be generated according to the calculation of the server. For example, a Todo list page needs to list specific executions in addition to displaying basic information. date, but only the execution date range and related rules are specified on the creation page, so the execution date is calculated and generated by the server based on the date range and rules.
+
+The following requirements will be fulfilled in a complex list:
+
+1. Immediately display the latest status on the list page when adding, editing and deleting list items, and update the data calculated by the server to this list item after the server responds;
+2. When the page is refreshed, the list page is always kept up to date;
+
+:::info example
+
+Stay tuned for complex list examples...
+
+:::
+
+### Free Mode
+
+In the above scenarios, you may want to judge whether to use the non-inductive interaction strategy or the most common conservative request strategy based on a condition. The requirements are as follows:
+
+1. When the network status is good, or paying users will use the non-sensing interaction strategy, but when the network fluctuates greatly, or free users cannot enjoy the non-sensing interaction strategy;
+2. Strategies can be switched freely;
+
+:::info example
+
+In the above examples, you can experience the free switching strategy
+
+:::
+
+## Not recommended application scenarios
+
+### Information sharing class
+
+The submitted information needs to be synchronized to others, such as order information. This type of information has high real-time requirements, and we should ensure that the submission is successful.
+
+### Complex data interaction class
+
+Complex data interaction refers to the mixed editing and filtering of data, such as adding, editing, deleting and filtering a certain list. In this case, Alova cannot currently support it well. In subsequent versions Will try to solve this puzzle too.
+
+## Technical solutions
+
+In the technical solution of non-inductive data interaction, alova has implemented data pre-fetching and silent submission respectively. Next, let's understand these two technical solutions.
+
+:::info
+
+Please make sure you have mastered the following chapters before reading
+
+- [Basic Learning](/next/tutorial/getting-started/quick-start)
+
+:::
+
+### Data pre-fetching
+
+In html, you may have seen such a tag ``, which tells the browser to preload the style file when it is idle, and put it in the cache In , when you really need to use it, you can take it out of the cache. Alova also uses a similar scheme to pre-fetch the required data through [useFetcher](/next/tutorial/client/strategy/use-fetcher), and it will be stored locally. in cache. You can predict the content that the user needs to read under any circumstances, and then pre-fetch the corresponding content. For example, the content of the next page can be pre-loaded in the process page, or the user stays on a button 200ms, we can pre-fetch the data needed for the next interface, which is similar to **Next.js** page preloading.
+
+
+
+### Silent submit
+
+Silent submission is a mechanism of submitting and responding. In the scheme, the completion of submission will be guaranteed, so it can be regarded as a safer optimistic update scheme. Silent submission mainly uses **Silent Queue** to persist request information and ensure request timing issues. **Virtual data** is used as a placeholder for server response data, which is replaced with actual response data when the request is completed. , through these two technologies, localized data creation is realized, and operations such as editing and deleting of newly created data are realized, even if the created data has not yet been submitted successfully on the server side. In order to keep development costs to a minimum, this is done automatically in alova.
+
+### Quiet Queue
+
+Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains related configurations for silent submission, such as _unique id_, _error retry parameters_, etc. The requests in the queue will only initiate the next request after the previous response, thus ensuring the timing of the requests in the queue. You can put dependent requests in the same queue, which also ensures data consistency.
+
+![Silent queue](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png)
+
+In the scheme, three behavior modes of `queue`, `silent`, and `static` are provided respectively, which are used to distinguish what kind of behavior a request needs to perform.
+
+- queue: The request will enter the silent queue, but it will not be persisted. It will wait for the previous request to complete before sending the request. The response callback will be triggered after the response. It is generally used for data acquisition that depends on the previous request;
+- silent: The request will enter the silent queue and be persisted, and then trigger the response callback immediately. In this behavior mode, onSuccess will receive virtual data, and onError will never be triggered. Use this pattern;
+- static: the request will not enter the silent queue, nor will it be persisted, it will issue the request immediately, and this mode can be used when silent submission is disabled;
+
+### virtual data
+
+In the submit-to-response mechanism, virtual data plays an important role. It means that before the server actually responds, it is used as a placeholder for the response data, and through the tracing mechanism, even if the virtual data is distributed in various locations of the application, Can be automatically replaced with the actual response data after the response. At the same time, it also plays an important role in the silent queue. It can identify the dependencies of requests in the queue, and replace the dependent data with actual data after the dependencies respond. For example, when creating a piece of data, it will return the id of this data. When the service When the terminal has not responded, the user performs a delete operation, and the id needs to be used as the delete identifier. At this time, the delete request will depend on the creation request. Before creating a request response, the virtual data will be used as an id placeholder as a parameter for deletion, and the virtual data id will be replaced after creating a request response, so that the deletion request can be completed.
+
+![virtual data](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png)
+
+Next, we will learn more about the characteristics of virtual data.
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json
similarity index 93%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json
rename to docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json
index 89d03fd9e..42291c7e5 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json
+++ b/docs/tutorial/03-client/01-strategy/09-sensorless-data-interaction/_category_.json
@@ -1,3 +1,3 @@
-{
- "label": "Sensorless data interaction"
-}
+{
+ "label": "Sensorless data interaction"
+}
diff --git a/docs/tutorial/03-client/01-strategy/10-use-captcha.md b/docs/tutorial/03-client/01-strategy/10-use-captcha.md
new file mode 100644
index 000000000..1d9530706
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/10-use-captcha.md
@@ -0,0 +1,179 @@
+---
+title: send captcha
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+The verification code sending hook saves you the trouble of developing the verification code sending function.
+
+
+
+## Features
+
+- The countdown will start automatically after the verification code is sent;
+- Custom countdown seconds;
+- Verification code sending limit;
+
+## Usage
+
+## Basic usage
+
+Demonstrates basic use of form hooks.
+
+
+
+
+```html
+
+
+
+
+
+
+```
+
+
+
+
+```jsx
+import { useState } from 'react';
+import { apiSendCaptcha } from './api.js';
+import { useCaptcha } from 'alova/client';
+
+const App = () => {
+ const [mobile, setMobile] = ref('');
+ const {
+ // send status
+ loading: sending,
+
+ // Call sendCaptcha to request the interface to send the verification code
+ send: sendCaptcha
+ } = useCaptcha(() => apiSendCaptcha(mobile));
+
+ return (
+
+ setMobile(target.value)}
+ />
+
+
+ );
+};
+```
+
+
+
+
+```html
+
+
+
+
+```
+
+
+
+
+By default, after the verification code is successfully sent, it will count down for 60 seconds, and calling `send` when the countdown is not over will throw an error.
+
+### Custom countdown seconds
+
+You can also customize the countdown seconds
+
+```javascript
+useCaptcha(() => apiSendCaptcha(mobile.value), {
+ //...
+ // highlight-start
+ // Set the countdown to 20 seconds
+ initialCountdown: 20
+ // highlight-end
+});
+```
+
+## API
+
+### Hook configuration
+
+Inherit all configurations of [**useRequest**](/next/api/core-hooks#userequest) except `immediate`, `immediate` in `useCaptcha` has been hard-coded to false.
+
+| Name | Description | Type | Default | Version |
+| ---------------- | ---------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- |
+| initialCountdown | Initial countdown, when the verification code is sent successfully, it will start countdown with this data | number | 60 | - |
+
+### Responsive data
+
+Inherit all responsive data from [**useRequest**](/next/api/core-hooks#userequest).
+
+| Name | Description | Type | Version |
+| --------- | ----------------------------------------------------------------------------------------------------------- | ------ | ------- |
+| countdown | The current countdown, -1 per second, the verification code can only be sent again after the countdown ends | number | - |
+
+### Action function
+
+Inherit all action functions of [**useRequest**](/next/api/core-hooks#userequest).
+
+| name | description | function parameters | return value | version |
+| ---- | ---------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------- |
+| send | Send a request, when the countdown is not over, the call will throw an error | Consistent with useRequest.send | Promise\ | - |
+
+### Event
+
+Inherit all events from [**useRequest**](/next/api/core-hooks#userequest).
diff --git a/docs/tutorial/03-client/01-strategy/11-use-serial-request.md b/docs/tutorial/03-client/01-strategy/11-use-serial-request.md
new file mode 100644
index 000000000..e2875532b
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/11-use-serial-request.md
@@ -0,0 +1,106 @@
+---
+title: useRequest with serial
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+This use hook is more concise and easy to use than [serial request in best practice](/next/tutorial/project/best-practice/serial-request), with unified loading status, error, and callback functions.
+
+## Features
+
+- A more concise and easy-to-use serial method;
+- Unified request status and callback function;
+- send function can trigger serial execution of multiple requests;
+
+
+
+## Usage
+
+### Basic usage
+
+Same usage as `useRequest`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request.
+
+```javascript
+import { useSerialRequest } from 'alova/client';
+
+const {
+ // Serial loading status, all requests will be changed to false
+ loading,
+
+ // The response data of the last request
+ data,
+
+ // Any request error will record the error message here
+ error,
+
+ // Manually send a serial request
+ send,
+
+ // serial request success callback binding function
+ onSuccess,
+
+ // Serial request error callback binding function, any request error will trigger it
+ onError,
+
+ // Serial request completion callback binding function
+ onComplete
+} = useSerialRequest(
+ [
+ // args is the parameter passed in by the send function
+ (...args) => request1(args),
+
+ // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second
+ (response1, ...args) => request2(response1, args),
+ (response2, ...args) => request3(response2, args)
+ ],
+ {
+ immediate: false
+ }
+);
+
+// emit request manually and pass arguments
+send(1, 2, 3);
+```
+
+It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function.
+
+```javascript
+useSerialRequest([
+ methodInstance,
+ (response1, ...args) => request2(response1, args),
+ (response2, ...args) => request3(response2, args)
+]);
+```
+
+### Request error
+
+When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error.
+
+## API
+
+### Hook configuration
+
+Inherit all configurations from [**useRequest**](/next/api/core-hooks#userequest).
+
+### Responsive data
+
+Inherit all responsive data from [**useRequest**](/next/api/core-hooks#userequest).
+
+### Action function
+
+Inherit all action functions of [**useRequest**](/next/api/core-hooks#userequest).
+
+### Event
+
+Inherit all events from [**useRequest**](/next/api/core-hooks#userequest).
diff --git a/docs/tutorial/03-client/01-strategy/12-use-serial-watcher.md b/docs/tutorial/03-client/01-strategy/12-use-serial-watcher.md
new file mode 100644
index 000000000..bf82a9dbb
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/12-use-serial-watcher.md
@@ -0,0 +1,107 @@
+---
+title: useWatcher with serial
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info policy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+Status update triggers a set of serial requests, which is more concise and easy to use than [serial request in best practice](/next/tutorial/project/best-practice/serial-request), with unified loading status, error, and callback functions.
+
+## Features
+
+- A more concise and easy-to-use serial method;
+- Unified request status and callback function;
+- Status update triggers serial execution of multiple requests;
+
+
+
+## Usage
+
+### Basic usage
+
+Same usage as `useWatcher`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request.
+
+```javascript
+import { useSerialWatcher } from 'alova/client';
+
+const {
+ // Serial loading status, all requests will be changed to false
+ loading,
+
+ // The response data of the last request
+ data,
+
+ // Any request error will record the error message here
+ error,
+
+ // Manually send a serial request
+ send,
+
+ // serial request success callback binding function
+ onSuccess,
+
+ // Serial request error callback binding function, any request error will trigger it
+ onError,
+
+ // Serial request completion callback binding function
+ onComplete
+} = useSerialWatcher(
+ [
+ // args is the parameter passed in by the send function
+ (...args) => request1(args),
+
+ // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second
+ (response1, ...args) => request2(response1, args),
+ (response2, ...args) => request3(response2, args)
+ ],
+ [watchedState1, watchedState2],
+ {
+ immediate: true
+ }
+);
+
+// Manually trigger the request and pass parameters
+send(1, 2, 3);
+```
+
+It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function.
+
+```javascript
+useSerialRequest([
+ methodInstance,
+ (response1, ...args) => request2(response1, args),
+ (response2, ...args) => request3(response2, args)
+]);
+```
+
+### Request error
+
+When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error.
+
+## API
+
+### Hook configuration
+
+Inherit all configurations of [**useWatcher**](/next/api/core-hooks#usewatcher).
+
+### Responsive data
+
+Inherit all responsive data from [**useWatcher**](/next/api/core-hooks#usewatcher).
+
+### Action function
+
+Inherit all action functions of [**useWatcher**](/next/api/core-hooks#usewatcher).
+
+### Event
+
+Inherit all events from [**useWatcher**](/next/api/core-hooks#usewatcher).
diff --git a/docs/tutorial/03-client/01-strategy/13-use-retriable-request.md b/docs/tutorial/03-client/01-strategy/13-use-retriable-request.md
new file mode 100644
index 000000000..83e34b04a
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/13-use-retriable-request.md
@@ -0,0 +1,231 @@
+---
+title: retriable request
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+A use hook that can automatically retry a request failure, you can use it for important requests.
+
+
+
+## Features
+
+- Customize the number of retries or judge whether retry is required according to the conditions;
+- Retry delay mechanism;
+- Manually stop retrying;
+
+## Usage
+
+### Basic usage
+
+```javascript
+import { useRetriableRequest } from 'alova/client';
+
+const {
+ // Loading status, always true during retry, until retry succeeds or fails
+ loading,
+
+ // response data
+ data,
+
+ // Request error information, every time a request or retry fails, there will be an error instance
+ // The last error instance will be overwritten
+ error,
+
+ // Every time a request or retry fails, the onError event will be triggered
+ onError,
+
+ // Request retry event, triggered immediately after each retry request is issued
+ onRetry,
+
+ // request retry failure event
+ // The request is not successful after reaching the maximum number of retries, or manually stopping retries will trigger
+ onFail,
+
+ // request or retry success event
+ onSuccess,
+
+ // Every request or retry, regardless of success or failure, will trigger the completion event
+ onComplete
+} = useRetriableRequest(request);
+```
+
+The maximum number of request retries for `useRetriableRequest` defaults to 3, and each retry will be delayed by 1 second. It will also make a request by default, you can change the behavior by setting `immediate` to false.
+
+### Set the static maximum number of retries
+
+The maximum number of retries indicates the maximum number of times to retry the request after the first request fails. During this period, if the request succeeds, it will stop continuing to retry. The default maximum number of retries is 3, and you can customize the settings in the following ways.
+
+When the request reaches the maximum number of retries and still fails, the `onFail` event will be triggered and the request retry will stop. If you want to continue to retry after the failure, you can call the `send` function, and it will perform a new round request and retry.
+
+```javascript
+const { send } = useRetriableRequest(request, {
+ //...
+ // highlight-start
+ // Set the maximum number of retries to 5
+ retry: 5
+ // highlight-end
+});
+```
+
+### Dynamically set the maximum number of retries
+
+Maybe sometimes you want to use a certain condition to determine whether to continue to retry. At this time, you can set `retry` as a function that returns a boolean value to dynamically determine whether to continue to retry.
+
+```javascript
+useRetriableRequest(request, {
+ //...
+ // highlight-start
+ // The first parameter is the last error instance, and the parameters passed in from the second parameter to send
+ retry(error, ...args) {
+ // If the request times out, continue to retry
+ return /network timeout/i.test(error.message);
+ }
+ // highlight-end
+});
+```
+
+### Set delay time
+
+The default retry delay time is 1 second, you can customize the setting in the following ways.
+
+```javascript
+useRetriableRequest(request, {
+ //...
+ backoff: {
+ // highlight-start
+ // Set the delay time to 2 seconds
+ delay: 2000
+ // highlight-end
+ }
+});
+```
+
+### Set an unfixed retry delay time
+
+Sometimes you want that the delay time of each request is not fixed, you can set the delay growth multiple in the following way, and the delay time will increase exponentially according to the number of retries.
+
+```javascript
+useRetriableRequest(request, {
+ //...
+ backoff: {
+ delay: 2000,
+ // highlight-start
+ // When the multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, the third is 8 seconds, and so on.
+ multiplier: 2
+ // highlight-end
+ }
+});
+```
+
+not enough? You can even add a random jitter value to each delay to make it look less regular.
+
+```javascript
+useRetriableRequest(request, {
+ //...
+ backoff: {
+ delay: 2000,
+ multiplier: 2,
+ // highlight-start
+ /**
+ * The initial jitter percentage value of the delay request, the range is 0-1
+ * When only startQuiver is set, endQuiver defaults to 1
+ * For example set to 0.5, it will add 50% to 100% random time on the current delay time
+ * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver
+ */
+ startQuiver: 0.5,
+
+ /**
+ * The jitter end percentage value of the delayed request, the range is 0-1
+ * When only endQuiver is set, startQuiver defaults to 0
+ * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time
+ * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver
+ */
+ endQuiver: 0.8;
+ // highlight-end
+ }
+});
+```
+
+### Manually stop retrying
+
+In some cases, you need to manually stop the retry, whether you are currently requesting or waiting for the next retry, you can use `stop` to stop it.
+
+```javascript
+const { stop } = useRetriableRequest(request, {
+ //...
+});
+
+const handleStop = () => {
+ stop();
+};
+```
+
+## API
+
+### Hook configuration
+
+Inherit all configurations from [**useRequest**](/next/api/core-hooks#userequest).
+
+| Name | Description | Type | Default | Version |
+| ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------- | ------- | --- |
+| retry | The maximum number of retries can also be set as a function returning a boolean value to dynamically determine whether to continue to retry. | number | (error: Error, ...args: any[]) => boolean | 3 | - |
+| backoff | Backoff policy, set retry delay time, etc. | [BackoffPolicy](#backoffpolicy) | - | - |
+
+### BackoffPolicy
+
+| Name | Description | Type | Default | Version |
+| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- |
+| delay | Delay time for another request, in milliseconds | number | 1000 | - |
+| multiplier | Specify the delay multiplier, for example, when multiplier is set to 2 and delay is 1 second, the first retry is 1 second, the second is 2 seconds, the third is 4 seconds, and so on | number | 0 | - |
+| startQuiver | The initial jitter percentage value of the delay request, ranging from 0-1. When only startQuiver is set, endQuiver defaults to 1. For example, if it is set to 0.5, it will increase the current delay time by 50% to 100% randomly Time, if endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver | number | 0 | - |
+| endQuiver | The jitter end percentage value of the delayed request, the range is 0-1, when onlyWhen endQuiver is set, startQuiver defaults to 0. For example, if it is set to 0.5, it will add a random time from 0% to 50% to the current delay time. If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver | number | 0 | - |
+
+### Responsive data
+
+Inherit all responsive data from [**useRequest**](/next/api/core-hooks#userequest).
+
+### Action function
+
+Inherit all action functions of [**useRequest**](/next/api/core-hooks#userequest).
+
+| name | description | function parameters | return value | version |
+| ---- | ------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | ------- |
+| stop | Stop retrying, it is only valid during retrying, and the onFail event will be triggered immediately after stopping | - | - | - |
+
+### Event
+
+Inherit all events from [**useRequest**](/next/api/core-hooks#userequest).
+
+| Name | Description | Callback Parameters | Version |
+| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- |
+| onRetry | Retry event bindings, they will fire after a retry is initiated | Retry event instance [RetriableRetryEvent](#retriableretryevent) | - |
+| onFail | Triggered when the request fails. It will be triggered when no more retries are made. For example, when the maximum number of retries is reached, when the retry callback returns false, manually call stop to stop retrying Note: 1 The .onError event will be triggered every time an error is reported. 2. If there are no retries, onError, onComplete and onFail will be triggered at the same time | Retry event instance [RetriableFailEvent](#retriablefailevent) | - |
+
+#### RetriableRetryEvent
+
+Event event instance inherited from alova.
+
+| Name | Description | Type | Default | Version |
+| ---------- | ----------------------------------- | ------ | -------- | ------- |
+| retryTimes | current retry times | number | required | - |
+| retryDelay | The delay time of this retry, in ms | number | required | - |
+
+#### RetriableFailEvent
+
+Event event instance inherited from alova.
+
+| Name | Description | Type | Default | Version |
+| ---------- | ------------------- | ------ | -------- | ------- |
+| retryTimes | current retry times | number | required | - |
diff --git a/docs/tutorial/03-client/01-strategy/14-use-sse.md b/docs/tutorial/03-client/01-strategy/14-use-sse.md
new file mode 100644
index 000000000..2d79edac4
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/14-use-sse.md
@@ -0,0 +1,247 @@
+---
+title: request by server-send events
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+A use hook that can automatically retry a request failure, you can use it for important requests.
+
+## Features
+
+- Simpler and easier-to-use usage.
+- Automatic connection management.
+
+## Usage
+
+
+
+
+```typescript
+import { useSSE } from 'alova/client';
+
+const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
+ initialData: 'initial-data' // Data initially in `data`
+});
+
+// Connect
+send('value');
+
+console.log(data.value); // Data is updated after receiving an event, by default it is `initialData`
+
+// Corresponds to the `message` event of `eventsource`
+const unbindMessage = onMessage(({ data }) => {
+ console.log(data);
+});
+
+const unbindError = onError(({ error }) => {
+ console.error('sse error', error);
+ close();
+});
+
+// Unbind when needed
+unbindMessage();
+unbindError();
+```
+
+
+
+
+```typescript
+import { useSSE } from 'alova/client';
+
+const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
+ initialData: 'initial-data' // Data initially in `data`
+});
+
+// Connect
+send('value');
+
+console.log(data); // Data is updated after receiving an event, by default it is `initialData`
+
+// Corresponds to the `message` event of `eventsource`
+const unbindMessage = onMessage(({ data }) => {
+ console.log(data);
+});
+
+const unbindError = onError(({ error }) => {
+ console.error('sse error', error);
+ close();
+});
+
+// Unbind when needed
+unbindMessage();
+unbindError();
+```
+
+
+
+
+```typescript
+import { useSSE } from 'alova/client';
+
+const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
+ initialData: 'initial-data' // Data initially in `data`
+});
+
+// Connect
+send('value');
+
+console.log(data); // Data is updated after receiving an event, by default it is `initialData`
+
+// Corresponds to the `message` event of `eventsource`
+const unbindMessage = onMessage(({ data }) => {
+ console.log(data);
+});
+
+const unbindError = onError(({ error }) => {
+ console.error('sse error', error);
+ close();
+});
+
+// Unbind when needed
+unbindMessage();
+unbindError();
+```
+
+
+
+
+:::warning
+
+Currently, `useSSE` can only connect to one source. This means that when attempting to connect to multiple targets, the previous connection will always be terminated.
+
+:::
+
+```typescript
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method);
+
+send('value1');
+// highlight-start
+send('value2'); // This will terminate the previous connection
+send('value3'); // This will also terminate the previous connection
+// highlight-end
+```
+
+By default, no request is sent. However, by setting immediate = true, you can skip the manual send step.
+
+```typescript
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
+ // highlight-start
+ immediate: true
+ // highlight-end
+});
+
+// codes here...
+```
+
+### Binding Custom Events
+
+```typescript
+const { data, readyState, onMessage, on } = useSSE(method);
+
+on('event-name', ({ data }) => {
+ console.log(data);
+});
+```
+
+### Global Response Interception
+
+By default, the response data is captured by [the global response interceptors](/next/tutorial/getting-started/basic/global-interceptor). If this is not the desired behavior, you can manually disable it.
+
+```typescript
+const { data, readyState, onMessage, on } = useSSE(method, {
+ // highlight-start
+ interceptByGlobalResponded: false // Now the data will not be intercepted by the response interceptor
+ // highlight-end
+});
+```
+
+## Type Declaration
+
+```typescript
+const enum SSEHookReadyState {
+ CONNECTING = 0,
+ OPEN = 1,
+ CLOSED = 2
+};
+
+type SSEHookConfig = {
+ /**
+ * Passed to new EventSource
+ */
+ withCredentials?: boolean;
+
+ /**
+ * Whether to be intercepted by the global responded interceptor of Alova instance
+ * @default true
+ */
+ interceptByGlobalResponded?: boolean;
+
+ /**
+ * Initial data
+ */
+ initialData?: any;
+
+ /**
+ * Whether to initiate the request immediately
+ * @default false
+ */
+ immediate?: boolean;
+};
+
+type SSEReturnType = {
+ readyState: ExportedType;
+ data: ExportedType;
+ eventSource: ExportedType;
+ /**
+ * Manually initiate the request. When `immediate: true` is used, this method is triggered automatically.
+ * @param sendArgs Request parameters passed to the method
+ */
+ send: (...sendArgs: any[]) => Promise;
+ /**
+ * Close the connection
+ */
+ close: () => void;
+ /**
+ * Register a callback function for the EventSource 'open' event
+ * @param callback Callback function
+ * @returns Function to unregister the callback
+ */
+ onOpen(callback: SSEOnOpenTrigger): () => void;
+
+ /**
+ * Register a callback function for the EventSource 'message' event
+ * @param callback Callback function
+ * @returns Function to unregister the callback
+ */
+ onMessage(callback: SSEOnMessageTrigger): () => void;
+
+ /**
+ * Register a callback function for the EventSource 'error' event
+ * @param callback Callback function
+ * @returns Function to unregister the callback
+ */
+ onError(callback: SSEOnErrorTrigger): () => void;
+
+ /**
+ * @param eventName Event name, defaults to 'open' | 'error' | 'message'
+ * @param handler Event handler
+ */
+ on(
+ eventName: string,
+ handler: (event: AlovaSSEMessageEvent) => void
+ ) => () => void;
+};
+```
diff --git a/docs/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md b/docs/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md
new file mode 100644
index 000000000..9a7887fa9
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/15-use-breakpoint-uploader.md
@@ -0,0 +1,5 @@
+---
+title: Breakpoint upload
+---
+
+coming soon...
diff --git a/docs/tutorial/03-client/01-strategy/16-use-uploader.md b/docs/tutorial/03-client/01-strategy/16-use-uploader.md
new file mode 100644
index 000000000..b1a36b1ba
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/16-use-uploader.md
@@ -0,0 +1,5 @@
+---
+title: Universal upload strategy
+---
+
+coming soon...
diff --git a/docs/tutorial/03-client/01-strategy/17-rate-limit-middleware.md b/docs/tutorial/03-client/01-strategy/17-rate-limit-middleware.md
new file mode 100644
index 000000000..e816220f7
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/17-rate-limit-middleware.md
@@ -0,0 +1,7 @@
+---
+title: Request rate limit
+---
+
+Set the number of requests that should be executed immediately for each interval, and other requests will be automatically delayed.
+
+coming soon...
diff --git a/docs/tutorial/03-client/01-strategy/README.md b/docs/tutorial/03-client/01-strategy/README.md
new file mode 100644
index 000000000..6a9c454d0
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/README.md
@@ -0,0 +1,21 @@
+---
+title: Client Strategy
+---
+
+import DocCardList from '@theme/DocCardList';
+
+Like using a component library, just learn it when you need a request strategy!
+
+All client use hooks have the following in common:
+
+1. They all rely on statesHook, please [set statesHook](/next/tutorial/getting-started/basic/combine-framework) before using.
+
+2. Their return values all contain the `update` function, which is used to actively update the exported state value.
+
+3. For performance under react, all operation functions such as `send`, `update`, `abort`, etc. are wrapped with `useCallback`.
+
+4. All binding functions starting with `on` can be chained.
+
+## Table of contents
+
+
diff --git a/docs/tutorial/03-client/01-strategy/_category_.json b/docs/tutorial/03-client/01-strategy/_category_.json
new file mode 100644
index 000000000..15c3c6fa5
--- /dev/null
+++ b/docs/tutorial/03-client/01-strategy/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Request Strategy"
+}
diff --git a/docs/tutorial/03-client/02-in-depth/01-update-across-components.md b/docs/tutorial/03-client/02-in-depth/01-update-across-components.md
new file mode 100644
index 000000000..793502e5e
--- /dev/null
+++ b/docs/tutorial/03-client/02-in-depth/01-update-across-components.md
@@ -0,0 +1,107 @@
+---
+title: Update states across components
+---
+
+:::info usage scope
+
+Client useHook
+
+:::
+
+There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated without resetting the situation. For edited content, `useFetcher` is no longer applicable.
+
+At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules.
+
+
+
+## Use method instance to find response states
+
+When determining the method instance corresponding to the updated response state, you can pass in this method instance in `updateState`. It will find whether there is a corresponding response state under this instance and provide it to you for modification in the callback function. Finally Just return the modified data.
+
+```javascript
+import { updateState } from 'alova';
+
+//Todo item being edited
+const editingTodo = {
+ id: 1,
+ title: 'todo1',
+ time: '09:00'
+};
+
+const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false });
+onSuccess(() => {
+ // highlight-start
+ // Fixed modification of todo data on the first page
+ // updateState will return whether the update is successful
+ const updated = updateState(getTodoList(1), todoList => {
+ return todoList.map(item => {
+ if (item.id === editingTodo.id) {
+ return {
+ ...item,
+ ...editingTodo
+ };
+ }
+ return item;
+ });
+ });
+ // highlight-end
+});
+```
+
+:::warning note
+
+1. When updating the state through `updateState`, if the cache (memory cache and persistent cache) is detected, the new data update cache will also be updated.
+2. Only when a request has been initiated using useRequest or useWatcher, alova will manage the states returned by the hook. The reason is that the response states is generated and saved through a Method instance, but when no request is initiated, the url and URL in the Method instance are Parameters such as params, query, and headers are still uncertain.
+
+:::
+
+## Dynamically update response states
+
+Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/next/tutorial/client/in-depth/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList.
+
+```javascript
+updateState('todoList', todoListRaw => {
+ todoListRaw.push({
+ title: 'new todo',
+ time: '10:00'
+ });
+ return todoListRaw;
+});
+```
+
+The [Method instance matcher](/next/tutorial/client/in-depth/method-matcher) will be introduced in detail in subsequent chapters.
+
+## Listen for matching events
+
+When dynamically updating the response state, sometimes you may want to do some processing when a method instance is matched, or you may want to obtain the matching method instance. `updateState` can also pass in a third parameter to set a matching event to achieve these purposes. .
+
+```javascript
+updateState(
+ 'todoList',
+ todoListRaw => {
+ // ...
+ },
+ {
+ // Called when a method instance is matched, and the parameter is the matched method instance.
+ onMatch: method => {
+ // ...
+ }
+ }
+);
+```
+
+:::warning ⚠️ Please make sure the component is not destroyed
+
+By default, `updateState` will look for the response state created by alova's useHooks when sending a request. However, to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response states has not been destroyed, otherwise the corresponding response states will not be found and the update will fail.
+
+This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions:
+
+1. Persist the page components to ensure that the updated states can still be found;
+2. Use [setCache](/next/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect.
+
+:::
+
+## Notes
+
+1. In actual use, whether you use `useRequest` or `useWatcher` to send a request, you can call the `send` function to specify different parameters to send the request repeatedly. The response states returned by these use hooks will be used by multiple method instances. Reference, so you can choose any method instance to match the same response states value;
+2. When dynamically searching and updating the response states, the method instance matcher finds multiple method instances, and the first instance will prevail;
diff --git a/docs/tutorial/03-client/02-in-depth/02-method-matcher.md b/docs/tutorial/03-client/02-in-depth/02-method-matcher.md
new file mode 100644
index 000000000..ad9415367
--- /dev/null
+++ b/docs/tutorial/03-client/02-in-depth/02-method-matcher.md
@@ -0,0 +1,87 @@
+---
+title: Method Matcher
+---
+
+:::info Usage scope
+
+Fully
+
+:::
+
+The method snapshot matcher is a method that dynamically searches for method instances in the requested method snapshot list. Each alova instance has an independent snapshot space. It is generally used when developers are not sure which method to use. The method snapshot matcher can be used to search according to certain rules.
+
+It is generally used with the following 5 functions that need to use method instances.
+
+1. [setCache](/next/tutorial/cache/set-and-query)
+2. [queryCache](/next/tutorial/cache/set-and-query)
+3. [invalidateCache](/next/tutorial/cache/manually-invalidate)
+4. [updateState](/next/tutorial/client/in-depth/update-across-components)
+5. [useFetcher.fetch](/next/tutorial/client/strategy/use-fetcher)
+
+## Matching rules
+
+When a method instance is requested, it will be saved as a snapshot. The method snapshot matcher searches for the method snapshots based on the `name` property set by the method instance. Multiple matchers are allowed to set the same `name`.
+
+## Matching by name property
+
+Match by passing in the full instance name. By default, a matching array is returned.
+
+```javascript
+// Each time getTodoList is called, a new method instance is generated, and their names are the same
+const getTodoList = currentPage =>
+ alova.Get('/todo/list', {
+ name: 'todoList'
+ // ...
+ });
+
+// Match all Method instances with the name `todoList`
+const matchedMethods = alova.snaptshots.match('todoList');
+```
+
+## Match by regular expression
+
+By passing in a regular expression for matching, all method instance names that match the regular expression will be matched, and the result is also an array.
+
+```javascript
+// Match all Method instances whose name starts with `todo`
+const matchedMethods = alova.snaptshots.match(/^todo/);
+```
+
+## Filter matching results
+
+By specifying `filter`, you can further filter method instances that do not meet the conditions. The filter function is used in the same way as Array.prototype.filter. Return true to indicate a successful match and false to indicate a failure. See the type declaration above for details.
+
+Let's look at a few examples.
+
+**Invalidate the cache of the last method instance of a specific name**
+
+```javascript
+const matchedMethods = alova.snaptshots.match({
+ name: 'todoList',
+ filter: (method, index, methods) => index === methods.length - 1
+});
+```
+
+## Match a single method instance
+
+You can also set the second function of the `match` function to `true` to return the first item of the matching result, and return `undefined` if no match is found.
+
+```js
+const matchedSingleMethod = alova.snaptshots.match(/^todo/, true);
+```
+
+## Limit instance snapshots
+
+By default, 1000 method instance snapshots are saved, otherwise frequent requests may cause memory overflow. You can also adjust the limit as needed.
+
+```js
+import { globalConfig } from 'alova';
+
+const alovaInstance = createAlova({
+ // ...
+ // Limit to 500 instance snapshots
+ snapshots: 500
+});
+```
+
+When set to 0, instance snapshots will no longer be saved, and the method snapshot matcher will not be used.
diff --git a/docs/tutorial/03-client/02-in-depth/03-middleware.md b/docs/tutorial/03-client/02-in-depth/03-middleware.md
new file mode 100644
index 000000000..3418ca85c
--- /dev/null
+++ b/docs/tutorial/03-client/02-in-depth/03-middleware.md
@@ -0,0 +1,268 @@
+---
+title: Request Middleware
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info Usage scope
+
+Client useHook
+
+:::
+
+You can set request middleware for all useHooks to freely control request behavior, providing powerful capabilities that can control almost all behaviors of a request. Whether it is a simple or complex request strategy, you may use it. Next, let's see what it can do.
+
+## Middleware function
+
+Request middleware is an asynchronous function. The following is a simple request middleware that prints some information before and after the request, without changing any request behavior.
+
+```javascript
+useRequest(todoList, {
+ async middleware(_, next) {
+ console.log('before request');
+ await next();
+ console.log('after requeste');
+ }
+});
+```
+
+The `next` function is an asynchronous function. Calling it will continue to send requests. At this time, the _loading_ state will be set to true, and then the request will be sent. The return value of next is a Promise instance with response data. You can manipulate the return value in the middleware function.
+
+## Ignore requests
+
+When you don't want to make a request, you can ignore the request by not calling `next`, as if the request has never been made. For example, do not make a request when a listening field changes in `useWatcher`.
+
+```js
+useWatcher(() => todoList(), [state1], {
+ middleware: async (_, next) => {
+ if (state1 === 'a') {
+ return next();
+ }
+ }
+});
+```
+
+## Control response data
+
+The return value of the middleware function will be used as the response data of this request for subsequent processing. If the middleware does not return any data but calls `next`, the response data of this request will be used for subsequent processing.
+
+```javascript
+// Convert response data and return
+useRequest(todoList, {
+ async middleware(_, next) {
+ const result = await next();
+ result.code = 500;
+ return result;
+ }
+});
+
+// The response data of this request will be used for subsequent processing
+useRequest(todoList, {
+ async middleware(_, next) {
+ await next();
+ }
+});
+
+// The string abc will be used as the response data
+useRequest(todoList, {
+ async middleware(_, next) {
+ await next();
+ return 'abc';
+ }
+});
+```
+
+## Change request
+
+Sometimes you want to change the request. At this time, you can specify another method instance in `next`. When sending the request, the information in this method will be requested. At the same time, you can also use `next` to set whether to force the request to penetrate the cache. This is also very simple.
+
+```javascript
+useRequest(todoList, {
+ async middleware(_, next) {
+ await next({
+ // Change the method instance of the request
+ method: newMethodInstance,
+
+ // Force the request this time
+ force: true
+ });
+ }
+});
+```
+
+## Control errors
+
+### Capture errors
+
+In the middleware, you can capture the request error generated in `next`. After capturing, the global `onError` hook will no longer be triggered.
+
+```javascript
+useRequest(todoList, {
+ async middleware(_, next) {
+ try {
+ await next();
+ } catch (e) {
+ console.error('Caught error', e);
+ }
+ }
+});
+```
+
+### Throw an error
+
+Of course, you can also throw a custom error in the middleware, and even if the request is normal, it will enter the request error process.
+
+```javascript
+// No request is sent, and global and request-level onError will be triggered. If the request is sent through `method.send`, a reject promise instance will be returned
+useRequest(todoList, {
+ async middleware(_, next) {
+ throw new Error('error on before request');
+ await next();
+ }
+});
+
+// After the request is successful, global and request-level onError will be triggered. If the request is sent through `method.send`, a reject promise instance will be returned
+useRequest(todoList, {
+ async middleware(_, next) {
+ await next();
+ throw new Error('error on after request');
+ }
+});
+```
+
+## Control response delay
+
+In the middleware, we can delay the response or respond in advance. In the case of early response, although the response data cannot be obtained, some other data can be returned as response data to participate in subsequent processing.
+
+```javascript
+// Delay response for 1 second
+useRequest(todoList, {
+ async middleware(_, next) {
+ await new Promise(resolve => {
+ setTimeout(resolve, 1000);
+ });
+ return next();
+ }
+});
+
+// Respond immediately and use string abc as response data
+useRequest(todoList, {
+ async middleware(_, next) {
+ return 'abc';
+ }
+});
+```
+
+## More than that
+
+**So far, we have mentioned the use of the second parameter `next` of the middleware, so what does the first parameter do? **
+
+The first parameter of the middleware contains some information about this request, as well as control functions for the status and events returned in useHook such as `loading`, `data` and `onSuccess`. Let's continue to look down!
+
+## Request information included
+
+```javascript
+async function middleware(context, next) {
+ // Method instance for this request
+ context.method;
+
+ // Parameter array sent by the send function, default is []
+ context.args;
+
+ // Cache data hit by this request
+ context.cachedResponse;
+
+ // Configuration collection of useHook
+ context.config;
+
+ // The various states returned by useHook, it is a state proxy, including the following properties
+ // loading, data, error, downloading, uploading, and additional states managed by managedStates
+ context.proxyStates;
+
+ // Operation function in current useHook, send, abort
+ // context.fetch in useFetcher
+ context.send;
+ context.abort;
+}
+```
+
+Next, let's take a look at what control capabilities are available.
+
+## Modify responsive data
+
+Use `context.proxyStates` to modify the state data of the current useHook. Since alova's useHook is compatible with multiple UI frameworks, proxyStates is a unified state proxy, which is used in a similar way to vue's ref value.
+
+```javascript
+async function middleware(context, next) {
+ const { loading, data } = context.proxyStates;
+
+ // Get loading value
+ const loadingValue = loading.v;
+ // Change loading status to true
+ loading.v = true;
+
+ // Modify data status value
+ data.v = {
+ /* ... */
+ };
+}
+```
+
+For detailed usage of state proxy, please refer to [State Proxy](/next/tutorial/advanced/custom/client-strategy).
+
+## Interrupt or repeat request
+
+The `abort` and `send` functions (`fetch` in useFetcher) received by the middleware can also send multiple requests when a request intent is triggered.
+
+A typical use case is request retry. If a request fails after sending a request, it will automatically request again according to a certain strategy. After the retry succeeds, it will trigger `onSuccess`. The following is a simple request retry example code.
+
+```javascript
+async function middleware(context, next) {
+ return next().catch(error => {
+ if (needRetry) {
+ setTimeout(() => {
+ context.send(...context.sendArgs);
+ }, retryDelay);
+ }
+ return Promise.reject(error);
+ });
+}
+```
+
+If you need to interrupt the request in the middleware, you can call `context.abort()`.
+
+## Controlled loading state
+
+In the above content, we know that we can modify responsive data through `loading.v`, but there will be obstacles when you modify the loading state value `loading`, because under normal circumstances, the loading state value will be automatically set to true when calling `next`, and automatically set to false in the response process, which will overwrite the loading state value modified by `loading.v`. At this time, we can turn on the controlled loading state. After turning it on, the loading state value will no longer be modified in the `next` function and the response process, and we will be fully controlled.
+
+Let's take request retry as an example. We hope that the loading state will remain true from the beginning of triggering a request intention, through request retry until the request ends.
+
+Use `context.controlLoading` to turn on the custom control loading state.
+
+```javascript
+async function middleware(context, next) {
+ context.controlLoading();
+ const { loading } = context.proxyStates;
+
+ // Set to true when the request starts
+ loading.v = true;
+ return next()
+ .then(value => {
+ // Set to false after the request is successful
+ loading.v = false;
+ return value;
+ })
+ .catch(error => {
+ if (needRetry) {
+ setTimeout(() => {
+ context.send(...context.sendArgs);
+ }, retryDelay);
+ } else {
+ // Set to false when no longer retrying
+ loading.v = false;
+ }
+ return Promise.reject(error);
+ });
+}
+```
diff --git a/docs/tutorial/03-client/02-in-depth/04-manage-extra-states.md b/docs/tutorial/03-client/02-in-depth/04-manage-extra-states.md
new file mode 100644
index 000000000..e332c012f
--- /dev/null
+++ b/docs/tutorial/03-client/02-in-depth/04-manage-extra-states.md
@@ -0,0 +1,197 @@
+---
+title: Manage Extra states
+---
+
+:::info Scope of usage
+
+Client useHook
+
+:::
+
+import Tabs from '@theme/Tabs';
+
+import TabItem from '@theme/TabItem';
+
+In the previous [Update responsive state across pages/modules](/next/tutorial/client/in-depth/update-across-components) chapter, we introduced how to update responsive states across pages or modules through `updateState`, but it can only update states created by useHooks. What should we do if we need to update custom states across components? Let's continue!
+
+## Update a single state
+
+You can manage additional states through `managedStates` when using useHooks, and automatically specify the state name to update it when calling `updateState` in other modules/pages.
+
+
+
+
+```javascript title="A.vue"
+const todoList = page =>
+ alova.Get('/todo', {
+ name: 'todoList'
+ });
+
+const allTodo = ref([]);
+useRequest(todoList, {
+ // ...
+
+ // highlight-start
+ // Manage allTodo as an additional state
+ managedStates: {
+ allTodo
+ }
+ // highlight-end
+});
+```
+
+```javascript title="B.vue"
+const handleSuccess = () => {
+ // highlight-start
+ // Pass in an object and specify the state name to find
+ updateState('todoList', {
+ allTodo: allTodoData => {
+ // Add a new todo item
+ allTodoData.push({
+ title: 'new todo',
+ time: '10:00'
+ });
+ return allTodoData;
+ }
+ });
+ // highlight-end
+};
+```
+
+
+
+
+
+```javascript title="A.jsx"
+const PageA = () => {
+ const todoList = page =>
+ alova.Get('/todo', {
+ name: 'todoList'
+ });
+
+ const [allTodo, setAllTodo] = allTodoState = useState([]);
+ useRequest(todoList, {
+ // ...
+
+ // highlight-start
+ // Manage allTodo as an additional state
+ managedStates: {
+ allTodo: allTodoState
+ }
+ // highlight-end
+ });
+
+ return (
+ // ...
+ );
+}
+```
+
+```javascript title="B.jsx"
+const PageB = () => {
+ // ...
+ const handleSuccess = () => {
+ // highlight-start
+ // Pass in an object and specify the state name to look up
+ updateState('todoList', {
+ allTodo: allTodoData => {
+ // Add a new todo item
+ allTodoData.push({
+ title: 'new todo',
+ time: '10:00'
+ });
+ return allTodoData;
+ }
+ });
+ // highlight-end
+ };
+
+ return (
+ // ...
+ );
+}
+```
+
+
+
+
+
+```javascript title="A.svelte"
+// a.svelte
+const todoList = page =>
+ alova.Get('/todo', {
+ name: 'todoList'
+ });
+
+const allTodo = writable([]);
+useRequest(todoList, {
+ // ...
+
+ // highlight-start
+ // Manage allTodo as an additional state
+ managedStates: {
+ allTodo
+ }
+ // highlight-end
+});
+```
+
+```javascript title="B.svelte"
+const handleSuccess = () => {
+ // highlight-start
+ // Pass in an object and specify the state name to search
+ updateState('todoList', {
+ allTodo: allTodoData => {
+ // Add a new todo item
+ allTodoData.push({
+ title: 'new todo',
+ time: '10:00'
+ });
+ return allTodoData;
+ }
+ });
+ // highlight-end
+};
+```
+
+
+
+
+
+## Update multiple states
+
+In the above example, we implemented the update of a single `allTodo` state across pages. In fact, any multiple states can be updated at the same time through the object description method of `updateState`.
+
+```javascript
+updateState('todoList', {
+ state1: state1Data => {
+ // ...
+ },
+ state2: state2Data => {
+ // ...
+ },
+ state3: state3Data => {
+ // ...
+ }
+ // ...
+});
+```
+
+It should be noted that the above 3 additional states need to be managed through the `managedStates` property before updating.
+
+## Abbreviation for data state update
+
+When only updating the data state, you can directly pass in the callback function without specifying it as an object.
+
+```javascript
+updateState('todoList', {
+ data: dataRaw => {
+ // ...
+ }
+});
+
+// The following is an abbreviation
+updateState('todoList', dataRaw => {
+ // ...
+});
+```
diff --git a/docs/tutorial/03-client/02-in-depth/_category_.json b/docs/tutorial/03-client/02-in-depth/_category_.json
new file mode 100644
index 000000000..5b186720e
--- /dev/null
+++ b/docs/tutorial/03-client/02-in-depth/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "In-Depth"
+}
diff --git a/docs/tutorial/03-client/_category_.json b/docs/tutorial/03-client/_category_.json
new file mode 100644
index 000000000..32faa7806
--- /dev/null
+++ b/docs/tutorial/03-client/_category_.json
@@ -0,0 +1,5 @@
+{
+ "label": "Client",
+ "collapsed": false,
+ "collapsible": false
+}
diff --git a/docs/tutorial/04-server/01-strategy/01-retry.md b/docs/tutorial/04-server/01-strategy/01-retry.md
new file mode 100644
index 000000000..593f7c7b0
--- /dev/null
+++ b/docs/tutorial/04-server/01-strategy/01-retry.md
@@ -0,0 +1,128 @@
+---
+title: Request retry strategy
+---
+
+:::info type
+
+server hook
+
+:::
+
+Request retry strategy, you can use it for important requests.
+
+## Features
+
+- Customize the number of retries or determine whether to retry based on conditions;
+
+- Retry delay mechanism;
+
+## Usage
+
+### Basic usage
+
+```javascript
+const { retry } = require('alova/server');
+const { alovaInstance } = require('./api');
+
+const request = alovaInstance.Get('/api/user');
+const hookedMethod = retry(request);
+```
+
+`retry`'s maximum request retry count defaults to 3, and each retry will be delayed by 1 second.
+
+### Set the maximum number of static retries
+
+The maximum number of retries indicates the maximum number of times a request is retried after the first request fails. If the request succeeds during this period, it will stop retrying. The default maximum number of retries is 3 times, and you can customize the setting in the following way.
+
+```javascript
+const hookedMethod = retry(request, {
+ // ...
+ // highlight-start
+ // Set the maximum number of retries to 5
+ retry: 5
+ // highlight-end
+});
+```
+
+### Dynamically set the maximum number of retries
+
+Sometimes you may want to determine whether to continue retrying based on a certain condition. In this case, you can set `retry` to a function that returns a boolean value to dynamically determine whether to continue retrying.
+
+```javascript
+const hookedMethod = retry(request, {
+ // ...
+ // highlight-start
+ // The first parameter is the last error instance, and the second parameter is the parameter passed in by send
+ retry(error) {
+ // If the request times out, continue to retry
+ return /network timeout/i.test(error.message);
+ }
+ // highlight-end
+});
+```
+
+### Set the delay time
+
+The default retry delay time is 1 second, and you can customize it in the following way.
+
+```javascript
+retry(request, {
+ // ...
+ backoff: {
+ // highlight-start
+ // Set the delay time to 2 seconds
+ delay: 2000
+ // highlight-end
+ }
+});
+```
+
+### Set an unfixed retry delay time
+
+Sometimes you want the delay time of each request to be not fixed. You can set the delay growth multiple as follows. The delay time will increase exponentially according to the number of retries.
+
+```javascript
+retry(request, {
+ // ...
+ backoff: {
+ delay: 2000,
+ // highlight-start
+ // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, the third is 8 seconds, and so on.
+ multiplier: 2
+ // highlight-end
+ }
+});
+```
+
+Not enough? You can even add a random jitter value to each delay time to make it look less regular.
+
+```javascript
+retry(request, {
+// ...
+backoff: {
+delay: 2000,
+multiplier: 2,
+// highlight-start
+/**
+* The starting percentage value of the delay request jitter, ranging from 0-1
+* When only startQuiver is set, endQuiver defaults to 1
+* For example, if it is set to 0.5, it will add a random time of 50% to 100% to the current delay time
+* If endQuiver has a value, the delay time will increase by a random value in the range of startQuiver and endQuiver
+*/
+startQuiver: 0.5,
+
+/**
+* The ending percentage value of the delay request jitter, ranging from 0-1
+* When only endQuiver is set, startQuiver defaults to 0
+* For example, if it is set to 0.8, it will add a random time of 0% to 80% to the current delay time
+* If startQuiver has a value, the delay time will increase by a random value in the range of startQuiver and endQuiver
+*/
+endQuiver: 0.8;
+// highlight-end
+}
+});
+```
+
+
diff --git a/docs/tutorial/04-server/01-strategy/02-send-captcha.md b/docs/tutorial/04-server/01-strategy/02-send-captcha.md
new file mode 100644
index 000000000..09eef7eab
--- /dev/null
+++ b/docs/tutorial/04-server/01-strategy/02-send-captcha.md
@@ -0,0 +1,7 @@
+---
+title: Send Captcha
+---
+
+Send the captcha via SMS or email, and record the countdown time according to the key. If you send the captcha again within the countdown, an error will be reported.
+
+coming soon...
diff --git a/docs/tutorial/04-server/01-strategy/03-rate-limit-middleware.md b/docs/tutorial/04-server/01-strategy/03-rate-limit-middleware.md
new file mode 100644
index 000000000..c9aab92cc
--- /dev/null
+++ b/docs/tutorial/04-server/01-strategy/03-rate-limit-middleware.md
@@ -0,0 +1,7 @@
+---
+title: Request Rate Limit
+---
+
+Set the number of requests that should be executed immediately per interval, other requests will be automatically delayed.
+
+Coming soon...
diff --git a/docs/tutorial/04-server/01-strategy/README.md b/docs/tutorial/04-server/01-strategy/README.md
new file mode 100644
index 000000000..8ea006bf3
--- /dev/null
+++ b/docs/tutorial/04-server/01-strategy/README.md
@@ -0,0 +1,24 @@
+---
+title: Server Strategy
+---
+
+import DocCardList from '@theme/DocCardList';
+
+Like using a component library, you can learn it when you need a specific request strategy!
+
+The following is the specification of server hooks, which receives a method instance and returns a new method instance, so it is very convenient to combine multiple server hooks.
+
+```ts
+/**
+ * Server hook model, representing the types of all server hooks.
+ * Pass a method or hooked method instance, set options, and return a hooked method instance.
+ * You can continue to modify this method to achieve the effect of combining multiple server hooks.
+ */
+export interface AlovaServerHook> {
+ (method: Method, options: Options): Method;
+}
+```
+
+## Table of contents
+
+
diff --git a/docs/tutorial/04-server/01-strategy/_category_.json b/docs/tutorial/04-server/01-strategy/_category_.json
new file mode 100644
index 000000000..15c3c6fa5
--- /dev/null
+++ b/docs/tutorial/04-server/01-strategy/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Request Strategy"
+}
diff --git a/docs/tutorial/04-server/_category_.json b/docs/tutorial/04-server/_category_.json
new file mode 100644
index 000000000..78716c63c
--- /dev/null
+++ b/docs/tutorial/04-server/_category_.json
@@ -0,0 +1,5 @@
+{
+ "label": "Server",
+ "collapsed": false,
+ "collapsible": false
+}
diff --git a/docs/tutorial/05-cache/01-mode.md b/docs/tutorial/05-cache/01-mode.md
new file mode 100644
index 000000000..8ed67bbb9
--- /dev/null
+++ b/docs/tutorial/05-cache/01-mode.md
@@ -0,0 +1,330 @@
+---
+title: Cache Mode
+---
+
+import MemoryCache from '@site/example-links/MemoryCache';
+import StoragePlaceholder from '@site/example-links/StoragePlaceholder';
+import StorageRestore from '@site/example-links/StorageRestore';
+
+Cache mode can be set at different granularities, such as global or request level. When set globally, all method instances created by the same alova instance will inherit the setting.
+
+:::info Note
+
+Whether to use cache mode and which cache mode to use depends on the scenario. The following will mention their usage scenarios when introducing different cache modes separately.
+
+:::
+
+## Memory Mode (Default)
+
+Memory mode belongs to the single-level cache (L1 cache) mode. By default, the cache is placed in memory. It is the most commonly used cache mode.
+
+```mermaid
+flowchart LR
+ A[User request] --> B{Check L1 cache}
+ B -->|Hit| C[Return data]
+ B -->|Miss| F[Request API interface]
+ F --> G[Update L1 cache]
+ G --> C
+ C --> H[End]
+
+ style F stroke-width:8px
+```
+
+By default, GET request has a memory cache time of 300000ms (5 minutes). Developers can also customize the cache setting. Please continue reading.
+
+### Client
+
+In the client, refreshing the page cache will invalidate it. The memory mode is generally used to solve the performance consumption caused by frequent requests for the same data in a short period of time (several minutes or seconds). For example, when you are writing a todo detail page, you may think that users will frequently click to view details in the todo list. If the user repeatedly views a certain detail, the interface will no longer be requested repeatedly, and the data can be returned immediately, which improves the response speed and reduces the server pressure. At this time, we can set the response data cache for a todo detail method instance.
+
+```javascript
+alovaInstance.GET('/todo/list', {
+ // ...
+ // highlight-start
+ cacheFor: {
+ // Set the cache mode to memory mode
+ mode: 'memory',
+
+ // Unit is milliseconds
+ // When set to `Infinity`, it means that the data will never expire. When set to 0 or a negative number, it means that it will not be cached
+ expire: 60 * 10 * 1000
+ }
+ // highlight-end
+});
+```
+
+Memory mode is the default mode. You can shorten it like this
+
+```javascript
+alovaInstance.GET('/todo/list', {
+ // ...
+ // highlight-start
+ cacheFor: 60 * 10 * 1000
+ // highlight-end
+});
+```
+
+### Server
+
+In the service, it is usually used in scenarios where the interface requires high access frequency and low latency to reduce the pressure on the downstream server. The usage method is the same as the client.
+
+Note when using it on the server:
+
+1. Too much cache will always consume server memory. You can use [lru-cache](https://www.npmjs.com/package/lru-cache) to control memory consumption.
+
+2. If you need to share cache in a single-machine nodejs cluster environment, you can use [@alova/psc](https://www.npmjs.com/package/@alova/psc) to create a cache solution for shared memory between processes.
+
+```js
+const { createPSCAdapter, NodeSyncAdapter } = require('@alova/psc');
+const { LRUCache } = require('lru-cache');
+
+function lRUCache(options = {}) {
+ const cache = new LRUCache(options);
+ return {
+ set(key, value) {
+ return cache.set(key, value);
+ },
+
+ get(key) {
+ return cache.get(key);
+ },
+
+ remove(key) {
+ return cache.delete(key);
+ },
+
+ clear() {
+ return cache.clear();
+ }
+ };
+}
+
+const alovaInstance = createAlova({
+ baseURL: 'https://api.alovajs.dev',
+
+ // Inter-process shared cache adapter
+ l1Cache: createPSCAdapter(
+ NodeSyncAdapter(),
+ lRUCache({
+ max: 1000,
+ ttl: 1000 * 60 * 10
+ })
+ )
+});
+```
+
+Memory cache mode corresponds to l1 cache. Here we replace the cache adapter with lru-cache shared between processes. You can also [customize the storage adapter](/next/tutorial/advanced/custom/storage-adapter). For example, when you only need a single-level cache, you can also directly set the l1 cache to the redis adapter.
+
+## Restore mode
+
+Restore mode corresponds to multi-level cache, namely L1 and L2 cache. After the restore mode is turned on, the response data will be stored in both L1 and L2 caches. When the L1 cache fails, the data will be read from the L2 cache and then the L1 cache will be updated. The interface will be requested again when the L2 cache also fails.
+
+```mermaid
+flowchart LR
+ A[User request] --> B{Check L1 cache}
+ B -->|Hit| C[Return data]
+ B -->|Miss| D{Check L2 cache}
+ D -->|Hit| E[Update L1 cache]
+ E --> C
+ D -->|Miss| F[Request API interface]
+ F --> G[Update L2 cache]
+ G --> E
+ C --> H[End]
+
+ style F stroke-width:8px
+```
+
+### Client
+
+In the client, when the cache has not expired, it will not be invalidated even if the page cache is refreshed. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of holidays each year are different, but will not change again. In this scenario, we only need to set the cache expiration time to the last minute of this year.
+
+When using alova in the client, `localStorage` is used as the L2 storage adapter by default. You can also [customize the storage adapter](/next/tutorial/advanced/custom/storage-adapter).
+
+Set on the method instance:
+
+```javascript
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ // ...
+ // highlight-start
+ cacheFor: {
+ // Set cache mode to persistence mode
+ mode: 'restore',
+ // Cache time
+ expire: 60 * 10 * 1000
+ }
+ // highlight-end
+});
+```
+
+#### What to do if data changes?
+
+When the method instance is set to restore mode, it may be necessary to allow users to re-cache the changed data after publishing the application due to changes in interface data or changes in the logic of front-end processing response data. At this time, you can set the cache tag through the `tag` attribute. Each piece of persistent data contains a `tag` identifier. When the `tag` changes, the original persistent data will become invalid, and new data will be obtained again and identified with a new `tag`.
+
+```javascript
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ // ...
+ cacheFor: {
+ mode: 'restore',
+ expire: 60 * 10 * 1000,
+
+ // highlight-start
+ // Add or modify tag parameters, cached data will become invalid
+ // It is recommended to use version number management
+ tag: 'v1'
+ // highlight-end
+ }
+});
+```
+
+### Server
+
+Generally used for multi-level cache, using L1 as memory cache and L2 as persistent cache, such as redis and memcached.
+
+Some application scenarios are as follows:
+
+1. High access frequency and low latency requirements: such as hot news and product details, which can further reduce network overhead and maintain faster response when the network is unstable.
+
+2. Reduce the pressure on downstream servers, such as services with peak access periods, and the upper-level cache can effectively reduce the pressure on the backend database and microservices.
+3. Integrate data merging and processing of multiple downstream servers. Multiple serial requests may lead to longer response time, and may also consume performance due to complex data conversion. The converted data can be cached.
+4. API rate limit and billing, weather forecast service API updates weather information once an hour, geographic location data API, etc.
+
+When using alova on the server, there is no L2 storage adapter by default. The implementation of file storage adapter and redis adapter are provided in [Server-side L2 storage practice](/next/tutorial/project/best-practice/l2-storage). You can also [customize storage adapter](/next/tutorial/advanced/custom/storage-adapter), for example, use MongoDB, MySQL and other databases as storage adapters for response data.
+
+:::warning Note
+
+When the request body is special data such as **FormData**, **Blob**, **ArrayBuffer**, **URLSearchParams**, **ReadableStream**, it will be considered that you have the intention to communicate with the server, and no caching will be performed in this case.
+
+:::
+
+## Set alova id
+
+Each cache key contains the namespace of the alova instance, in the following format:
+
+```
+cacheKey = [prefix][alova-id][method-json-string]
+```
+
+By default, [alova-id] increments in the order in which alova is created. When using persistent cache in a server environment, it is strongly recommended to set the namespace of the alova id fixed cache key. **This is particularly important**, otherwise the order in which multiple alova instances are created may change, resulting in the inability to match the corresponding cache.
+
+```js
+const userAlova = createAlova({
+ // ...
+ id: 'user'
+});
+
+const orderAlova = createAlova({
+ // ...
+ id: 'order'
+});
+```
+
+## Set cache mode globally
+
+If you need to set the cache mode globally, you can do it as follows:
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ cacheFor: {
+ // Set the cache mode for POST uniformly
+ POST: {
+ mode: 'restore',
+ expire: 60 * 10 * 1000
+ },
+ // Set the cache mode for HEAD requests uniformly
+ HEAD: 60 * 10 * 1000
+ }
+ // highlight-end
+});
+```
+
+After this, method instances created through the `alovaInstance` instance will use this cache setting by default, and you can also override it in the method instance.
+
+## Globally turn off cache mode
+
+If you don't want to use anyIf you want to use any request cache, you can turn it off globally. If you want to use it only for a few specific requests, you can also turn it off globally and set it in the specified method instance.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ // Set to null to globally turn off all request caches
+ cacheFor: null
+ // highlight-end
+});
+```
+
+## Set different expiration times
+
+In restore mode, you can also set different expiration times for L1 and L2 caches. Set `expire` to a function, which will be called when setting L1 and L2 caches respectively.
+
+The following is an example of setting different expiration times for a single request.
+
+```js
+alovaInst.Get('/user/profile', {
+ // ...
+ cacheFor: {
+ mode: 'restore',
+ expire: ({ method, mode }) => {
+ // Set 5 minutes cache in l1 cache and 1 day cache in l2 cache
+ return mode === 'memory' ? 5 * 60 : 24 * 60 * 60;
+ }
+ }
+});
+```
+
+The following is an example of setting different expiration times for GET requests globally, when the method metadata has the `setDiffExpire` flag.
+
+```js
+const alovaInst = createAlova({
+ // ...
+ cacheFor: {
+ GET: {
+ mode: 'restore',
+ expire: ({ method, mode }) => {
+ if (method.meta.setDiffExpire) {
+ // Set 5 minutes cache in l1 cache and 1 day cache in l2 cache
+ return mode === 'memory' ? 5 * 60 : 24 * 60 * 60;
+ }
+ return 5 * 60;
+ }
+ }
+ }
+});
+```
+
+## Expiration time type
+
+There are two types of expiration time to choose from, namely **relative time** and **absolute time**
+
+### Relative time
+
+That is, the expiration time starts when the cache data is saved, in **milliseconds**. The above examples are all of this type.
+
+```javascript
+cacheFor: 60 * 10 * 1000;
+```
+
+```javascript
+cacheFor: {
+ expire: 60 * 10 * 1000,
+}
+```
+
+### Absolute time
+
+With a specific time point as the expiration time, the cache will expire at the set time point
+
+```javascript
+cacheFor: new Date('2030-01-01');
+```
+
+```javascript
+cacheFor: {
+ expire: new Date('2030-01-01');
+}
+```
+
+## Automatic response maintenance instructions
+
+The key of the response data cache is a combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance as a unique identifier. Any information or location will be treated as a different key. If you want to customize the cache key, you can refer to [Custom method key](/next/tutorial/advanced/in-depth/custom-method-key).
diff --git a/docs/tutorial/05-cache/02-auto-invalidate.md b/docs/tutorial/05-cache/02-auto-invalidate.md
new file mode 100644
index 000000000..54c968281
--- /dev/null
+++ b/docs/tutorial/05-cache/02-auto-invalidate.md
@@ -0,0 +1,105 @@
+---
+title: Auto Invalidate
+---
+
+Automatic invalidation cache is to set the matching rules of the invalidation source method in the target cache. When the source method is successfully requested, the target cache will be automatically matched and invalidated, and there is no need to manually clear the cache. When the target cache and the invalidation source are one-to-one or one-to-many, it is very convenient to set the automatic invalidation rule.
+
+```mermaid
+flowchart
+ M1[method1 invalidation source pointing] --> T1[target cache]
+ M11[method1 invalidation source pointing] --> T2[target cache]
+ M2[method2 invalidation source pointing] --> T2[target cache]
+ MN[methodN invalidation source pointing] --> T2[target cache]
+```
+
+## Usage scenarios
+
+1. After editing a list item and submitting it successfully, the cache data of the list item is automatically invalidated.
+2. On the server side, for example, the current cache needs to be invalidated after the user's personal information, configuration data, etc. are updated.
+
+## Set automatic invalidation rules
+
+It is very simple to set this rule. You can set the `hitSource` parameter for it when creating a Method instance with cache.
+
+### Set the invalidation source to the method instance
+
+Use a fixed method instance as the invalidation source. As long as the request of this method instance or its cloned instance succeeds, the target cache will be automatically cleared.
+
+```javascript
+alova.Get('/todo/1', {
+ // ...
+ hitSource: alova.Post('/todo', {})
+});
+```
+
+### Match the invalidation source by method name
+
+Like the method matcher, you can specify the method name in hitSource to match the invalidation source. Multiple invalidation sources can be set to the same name. When the method instance with this name is requested successfully, the target cache will be automatically cleared.
+
+```javascript
+const methodSubmitTodo = data =>
+ alova.Post('/todo', data, {
+ name: 'submitTodo'
+ });
+
+alova.Get('/todo/1', {
+ // ...
+ // Match the invalidation source whose method instance name is submitTodo
+ hitSource: 'submitTodo'
+});
+```
+
+### Match invalidation source by method name regular expression
+
+If the method instance name is not fixed, you can specify a regular expression in hitSource to match the method name. When the matched method instance is requested successfully, the target cache will be automatically cleared.
+
+```javascript
+const methodSubmitTodo = data =>
+ alova.Post('/todo', data, {
+ name: 'prefix-submitTodo'
+ });
+
+alova.Get('/todo/1', {
+ // ...
+ // Match all instances of method instance name starting with prefix
+ hitSource: /^prefix/
+});
+```
+
+### Combining failure sources
+
+If you want to use multiple rules above to match failure sources, you can specify hitSource as an array, where the array item is any of the above 3 rules, and method instances that meet any of the rules in the array will be matched.
+
+```javascript
+alova.Get('/todo/1', {
+ // ...
+ // When the method instance request that satisfies any matching rule in the array is successful, this cache will be invalidated
+ hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/]
+});
+```
+
+## Automatic invalidation scope
+
+Automatic invalidation will search for caches under all alova instances by default. Too many invalidation targets may lead to poor performance. If you want to control the scope of automatic invalidation or turn it off, you can set it as follows.
+
+```js
+import { globalConfig } from 'alova';
+
+globalConfig({
+ /**
+ * Automatic hit cache switch.
+ * There are three options here:
+ * - global: invalidate cache across alova instances.
+ * - self: only invalidate caches from the same alova instance.
+ * - close: no longer automatically invalidate cache.
+ * Defaults to 'global'
+ */
+ autoHitCache: 'self'
+});
+```
+
+## hitSource data type
+
+```typescript
+type hitSource = string | RegExp | Method | (string | RegExp | Method)[];
+```
diff --git a/docs/tutorial/05-cache/03-manually-invalidate.md b/docs/tutorial/05-cache/03-manually-invalidate.md
new file mode 100644
index 000000000..26df38876
--- /dev/null
+++ b/docs/tutorial/05-cache/03-manually-invalidate.md
@@ -0,0 +1,98 @@
+---
+title: Manual Invalidate
+---
+
+Generally, automatic cache invalidation is more concise and is recommended to be used first to invalidate the cache. When automatic cache invalidation does not meet the needs, you can also invalidate the cache by calling `invalidateCache`.
+
+## Invalidate a single cache
+
+Pass a method instance to the `invalidateCache` function, and it will find the cache under this instance for invalidation.
+
+In the following example, when the submission is successful, the cache of this todo detail data will be invalidated.
+
+```javascript
+// Get the todo detail data with id 1
+const getTodoDetail = id =>
+ alovaInstance.Get(`/todo/${id}`, {
+ cacheFor: 600 * 1000
+ });
+const { loading, data } = useRequest(getTodoDetail(1));
+```
+
+```javascript
+// Submit the data and invalidate the todo detail data with id 1.
+const {
+ // ...
+ send,
+ onSuccess
+} = useRequest(createTodoPoster, { immediate: false });
+
+// highlight-start
+// Invalidate cache after successful submission
+onSuccess(() => {
+ invalidateCache(getTodoDetail(1));
+});
+// highlight-end
+
+const handleSubmit = () => {
+ send({
+ title: 'new todo',
+ content: 'new todo content'
+ });
+};
+```
+
+## Invalidate multiple caches
+
+You can also pass in an array of method instances to invalidate multiple caches.
+
+```javascript
+invalidateCache([method1, method2, ...]);
+```
+
+## Dynamically invalidate cache
+
+Sometimes you may not be sure which cache data needs to be invalidated. We can use [method snapshot matcher](/next/tutorial/client/in-depth/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first five instances of a method called todoList.
+
+```javascript
+const getTodoList = currentPage => {
+ return alovaInstance.Get('/todo/list', {
+ // highlight-start
+ // Set the name for the method instance first, which is used to filter out the required Method instance when the Method instance cannot be directly specified
+ name: 'todoList',
+ // highlight-end
+ params: {
+ currentPage,
+ pageSize: 10
+ }
+ });
+};
+
+const {
+ // ...
+ send,
+ onSuccess
+} = useRequest(createTodoPoster, { immediate: false });
+// After successful submission, the todo data cache of the first page is fixedly invalidated
+onSuccess(() => {
+ // highlight-start
+ // Invalidate the cache of the first 5 Method instances named todoList
+ const matchedMethods = alovaInstance.snapshots.match({
+ name: 'todoList',
+ filter: (method, index, ary) => {
+ return index < 5;
+ }
+ });
+ invalidateCache(matchedMethods);
+ // highlight-end
+});
+```
+
+> For more usage of method matchers, see [method snapshot matcher](/next/tutorial/client/in-depth/method-matcher)
+
+## Invalidate all caches
+
+```javascript
+// When no parameters are passed, invalidate all response caches
+invalidateCache();
+```
diff --git a/docs/tutorial/05-cache/04-force-request.md b/docs/tutorial/05-cache/04-force-request.md
new file mode 100644
index 000000000..30494990f
--- /dev/null
+++ b/docs/tutorial/05-cache/04-force-request.md
@@ -0,0 +1,17 @@
+---
+title: Forced Request
+---
+
+Forced request is a mechanism that bypasses cache checks to trigger request sending. It is useful when you need to get the latest data under certain conditions.
+
+## Forced request in method
+
+Forced request by calling the send function of the method instance and passing true.
+
+```javascript
+const response = await alovaInstance.Get('/api/user').send(true);
+```
+
+## Forced request in useHook
+
+Please go to [Automatically manage request status-Forced request](/next/tutorial/client/strategy/use-request) for details.
diff --git a/docs/tutorial/05-cache/05-set-and-query.md b/docs/tutorial/05-cache/05-set-and-query.md
new file mode 100644
index 000000000..84e1f0fa4
--- /dev/null
+++ b/docs/tutorial/05-cache/05-set-and-query.md
@@ -0,0 +1,88 @@
+---
+title: Set & Query Cache
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Cache also supports update and search. In [cache mode](/next/tutorial/cache/mode), we mentioned that each cache data is saved with the method instance that sent the request as the key, so when manually updating the cache, the method instance will also be used to search for the corresponding cache data.
+
+## Update cache
+
+### Update static values
+
+Use `setCache` to update cache data. Its first parameter is the method instance, the second is the new cache data, and returns a Promise instance to indicate whether the execution is completed.
+
+```js
+import { setCache } from 'alova';
+await setCache(todoDetail(id), detailedData);
+```
+
+### Dynamic cache update
+
+You can also pass a callback function in `setCache` to dynamically calculate cache data and return the cache data to be updated. If `undefined` is returned in the function, the cache update will be aborted.
+
+```javascript
+await setCache(todoDetail(id), oldCache => {
+ if (!oldCache.allowUpdate) {
+ return; // Return undefined to abort cache update
+ }
+
+ // Return the data to be cached
+ return {
+ ...oldCache,
+ name: 'new name'
+ };
+});
+```
+
+### Update strategy
+
+When the passed method sets multi-level cache, the L1 cache and L2 cache will be updated by default. You can control the update of the specified cache separately through `policy`.
+
+```js
+await setCache(todoDetail(id), detailedData, {
+ /**
+ * Cache policy.
+ * - l1: Update only l1 cache.
+ * - l2: Update only l2 cache.
+ * - all: Update l1 cache and update l2 cache (method cache mode needs to be 'restore').
+ * @default 'all'
+ */
+ policy: 'l1'
+});
+```
+
+## Query cache
+
+Query cached data through the `queryCache` method, which receives a method instance.
+
+```javascript
+import { queryCache } from 'alova';
+
+const cachedData = await queryCache(getTodoListByDate('2022-10-01'));
+```
+
+You can also dynamically find method instances through [method snapshot matchers](/next/tutorial/client/in-depth/method-matcher).
+
+```javascript
+const lastMethod = alovaInstance.snapshots.match('todoList', true);
+const cachedData = lastMethod ? await queryCache(lastMethod) : undefined;
+```
+
+### Query strategy
+
+When the incoming method is set with multi-level cache, the default is to query the L1 cache first, then the L2 cache. You can use `policy` to control only the specified cache.
+
+```js
+const cachedData = await queryCache(getTodoListByDate('2022-10-01'), {
+ /**
+ * Cache strategy.
+ * - l1: query only the l1 cache.
+ * - l2: query only the l2 cache.
+ * - all: query the l1 cache and query the l2 cache (the method cache mode needs to be 'restore').
+ * @default 'all'
+ */
+ policy: 'l1'
+});
+```
diff --git a/docs/tutorial/05-cache/06-controlled-cache.md b/docs/tutorial/05-cache/06-controlled-cache.md
new file mode 100644
index 000000000..4d174c708
--- /dev/null
+++ b/docs/tutorial/05-cache/06-controlled-cache.md
@@ -0,0 +1,55 @@
+---
+title: Controlled Cache
+---
+
+When sending a request, the default response cache will be matched first. In some cases, you may need to use `IndexedDB` as a cache management solution and use a custom `IndexedDB` adapter, but this will make all requests use it as a storage solution, while controlled cache allows you to control the custom cache from a single request.
+
+Using controlled cache is also very simple. You can set `cacheFor` in method to an asynchronous or synchronous function. The data returned in this function will be used as cache data. For example, read data in `IndexedDB`.
+
+```javascript
+const getFile = fileName =>
+ alovaInstance.GET(`/file/${fileName}`, {
+ // Controlled cache functions support asynchronous and synchronous functions
+ cacheFor() {
+ return new Promise((resolve, reject) => {
+ const tx = db.transaction(['files']);
+ const getRequest = tx.objectStore('files').get(fileName);
+ getRequest.onsuccess = resolve;
+ getRequest.onerror = reject;
+ });
+ }
+ });
+```
+
+You can also return `undefined` or no data in `cacheFor` and continue to send the request, which is useful in the case of cache misses when customizing cache management.
+
+## Use transform to set cache
+
+Because the `transform` function has the following two features:
+
+- It is triggered only when responding, and will not be triggered when hitting the response cache;
+
+- It supports asynchronous functions;
+
+Therefore, you can also use it to save custom caches. For example, in the scenario where files are used as response data for caching, you can use IndexedDB to cache file data.
+
+```javascript
+const fileGetter = alovaInstance.Get('/file/file_name', {
+ // Use IndexedDB to cache files
+ async transform(fileBlob) {
+ await new Promise((resolve, reject) => {
+ const tx = db.transaction(['files'], 'readwrite');
+ const putRequest = tx.objectStore('files').put({
+ file: fileBlob
+ });
+ putRequest.onsuccess = resolve;
+ putRequest.onerror = reject;
+ });
+ return fileBlob;
+ }
+});
+```
+
+## Notes
+
+When used in usehooks, throwing an error in the `cacheFor` function will trigger `onError`. When using the method instance to directly initiate a request, the promise will be rejected.
diff --git a/docs/tutorial/05-cache/README.md b/docs/tutorial/05-cache/README.md
new file mode 100644
index 000000000..5180903b6
--- /dev/null
+++ b/docs/tutorial/05-cache/README.md
@@ -0,0 +1,11 @@
+---
+title: Response Cache Details
+---
+
+Response caching allows us to reuse response data and avoid repeated requests, thereby reducing response time and saving server resources. Alova supports multi-level caching and two caching modes: **memory mode and restore mode**. Just choose the one that suits your needs.
+
+On the client side, response caching allows users to see data immediately without any delay. On the server side, response data can be cached for a short time to solve the hot key problem.
+
+In addition, using the cache operation API, you can also freely add, modify and delete caches, as well as customize cache matching rules.
+
+Next, let's start with the cache mode!
diff --git a/docs/tutorial/05-cache/_category_.json b/docs/tutorial/05-cache/_category_.json
new file mode 100644
index 000000000..0a5fe043b
--- /dev/null
+++ b/docs/tutorial/05-cache/_category_.json
@@ -0,0 +1,5 @@
+{
+ "label": "Cache Details",
+ "collapsed": false,
+ "collapsible": false
+}
diff --git a/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md b/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md
new file mode 100644
index 000000000..98425283b
--- /dev/null
+++ b/docs/tutorial/06-advanced/01-in-depth/05-custom-method-key.md
@@ -0,0 +1,52 @@
+---
+title: Custom Method Key
+---
+
+:::info Usage scope
+
+Fully
+
+:::
+
+Method key is used to identify all data associated with method instances and has a great effect, for example:
+
+- Caching of associated response data
+- Identity sharing request
+- Associate the states returned by useHooks such as `useRequest`, so that it can match these states with `updateState`.
+
+By default, the method key is generated from the relevant request parameters of the method instance, which can accurately identify a request.
+
+But sometimes you want to change it so that the above three situations can be recognized as the same method in different requests.
+
+## Customize method instance key
+
+```javascript
+// Method key is generated when it is created, you can customize it through key
+const methodInst = alovaInstance.Get('/api/user', {});
+methodInst.key = 'my-custom-method-key';
+```
+
+## Customize all method instance keys
+
+The method key is generated through `Method.prototype.generateKey`, you can override this method to change the method key generation rules.
+
+```javascript
+import { Method } from 'alova';
+
+Method.prototype.generateKey = function () {
+ return 'your-custom-method-key';
+};
+```
+
+You can also set different generation rules according to the alova instance.
+
+```javascript
+Method.prototype.generateKey = function () {
+ if (this.context === alovaInstance1) {
+ return 'alova-1-method-key';
+ } else {
+ // ...
+ }
+ return 'alova-default-method-key';
+};
+```
diff --git a/docs/tutorial/06-advanced/01-in-depth/07-cache-logger.md b/docs/tutorial/06-advanced/01-in-depth/07-cache-logger.md
new file mode 100644
index 000000000..c655c845b
--- /dev/null
+++ b/docs/tutorial/06-advanced/01-in-depth/07-cache-logger.md
@@ -0,0 +1,59 @@
+---
+title: Cache Logger
+---
+
+:::info Usage scope
+
+Fully
+
+:::
+
+In order to facilitate debugging when using the interface cache, when the request hits the cache without sending a network request, the hit cache information will be printed on the console by default, which can solve some confusion when using the cache.
+
+If you don't want to print cache information or custom control print cache information in some cases (such as production environment), you can also close it.
+
+## Close cache logger printing
+
+Console printing can be turned off by setting `cacheLogger` to `false/null` when creating an alova instance.
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ cacheLogger: false
+});
+```
+
+You can also dynamically turn it on and off according to different environments.
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ // Enable cache logger in the development environment
+ cacheLogger: process.env.NODE_ENV === 'development'
+});
+```
+
+## Custom print cache logger
+
+The cache logger is printed via `console.log` by default. If `console.log` or other purposes are not supported in your project environment, `cacheLogger` can be specified as a function to customize the logger for processing cache hits.
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ /**
+ * Custom cache logger function
+ * @param response hit cache data
+ * @param method the current method instance
+ * @param cacheMode cache mode memory or restore
+ * @param tag The tag in the restore mode has a value only when the tag is set in the corresponding cache
+ */
+ cacheLogger(response, method, cacheMode, tag) {
+ saveHitCache({
+ response,
+ method,
+ cacheMode,
+ tag
+ });
+ }
+});
+```
diff --git a/docs/tutorial/06-advanced/01-in-depth/09-ssr.md b/docs/tutorial/06-advanced/01-in-depth/09-ssr.md
new file mode 100644
index 000000000..a58aadfd8
--- /dev/null
+++ b/docs/tutorial/06-advanced/01-in-depth/09-ssr.md
@@ -0,0 +1,219 @@
+---
+title: Server-Side Rendering(SSR)
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Overview
+
+Although the positioning of alova is not to make requests in nodejs, we have also adapted it in order to combine the server-side rendering([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)) of the UI framework. Although built-in request functionality is provided in e.g. `Nuxt3.x`, `Sveltekit`, if you choose to use alova, you can use alova to manage requests in both server and client, instead of server and client separately. Use different request schemes to manage them.
+
+Here are some caveats for using alova in SSR, and examples of usage in SSR for different UI frameworks.
+
+## Call apis on server
+
+In SSR, it is necessary to get data on server and render it into HTML. In this case, we cannot use alova's use hooks (and do not need to use them) to obtain data. Below we will show the supported SSR frameworks respectively.
+
+### Nuxt3.x
+
+In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `useFetch` and `$fetch` request functions are also provided. These request functions that can be used on both server and client are really convenient. However, if you want to use alova in nuxt, you can use the combination of **useAsyncData + alova.Method** to complete the server-side data fetching, which is no different from your usual `useAsyncData`.
+
+```html
+
+```
+
+### Nextjs
+
+Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/next/tutorial/getting-started/quick-start) call apis in the function.
+
+```jsx
+export const getServerSideProps = async ctx => {
+ const list = await alovaInstance.Get('/todo/list', {
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ }
+ });
+ return {
+ props: {
+ list
+ }
+ };
+};
+export default function App(props) {
+ return props.list.map(item => (
+
+ {item.title}
+ {item.time}
+
+ ));
+}
+```
+
+### Sveltekit
+
+Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/next/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`.
+
+```javascript title=+page.server.js
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ }
+});
+
+/** @type {import('./$types').PageServerLoad} */
+export async function load({ params }) {
+ return {
+ list: todoListGetter
+ };
+}
+```
+
+## Using usehooks in SSR
+
+Since each SSR framework has its own way to initialize data on server, when generating html in SSR, `useRequest` and `useWatcher` in the component will not be initiated even if `immediate` is set to `true` request, as this is more like client initialization data.
+
+However, if you need to initialize the data of the page as in the client, you can also set `immediate` to `true`, and when the page is running in the browser, you can use all the functions of alova as usual.
+
+## Precautions
+
+### Client and server caches are separate
+
+If you use alova's caching function, you may need to pay attention here that the client and server caches are not shared, which means that if you directly use **usehooks** to get data when initializing the page, you may Run into inconsistencies in client-side and server-side rendering, although few people do.
+
+Please see the following code snippet.
+
+
+
+
+```html
+
+
+```
+
+
+
+
+The following code assumes that `alovaGetter` requests are cached on server, but not on the client.
+
+At this time, when the html is generated on server , `loading` is `false` and does not display `
loading
` because it hits the cache, but when the client is initialized, because it misses the cache, `loading` is `true` will cause `
loading
` to be displayed, and the SSR framework will prompt that the rendering of the two ends is inconsistent.
+
+**Solution**
+
+1. Try to put the page data initialization work in the acquisition function instead of the component;
+2. If you must do this, you can avoid using the same apis on the client and server, or turn off the problematic api caches;
+3. If caching is also required, you can clear the cache on server in the data initialization function of server. The sample code is as follows:
+
+
+
+
+```html
+
+
+ >
+ );
+}
+
+export const getServerSideProps = async () => {
+ // Clear the cache on server
+ invalidateCache(alovaGetter);
+ return {
+ props: {}
+ };
+};
+```
+
+
+
+
+```javascript title=+page.server.js
+import { invalidateCache } from 'alova';
+
+/** @type {import('./$types').PageServerLoad} */
+export async function load({ params }) {
+ // Clear the cache on server
+ invalidateCache(alovaGetter);
+ return {};
+}
+```
+
+
+
diff --git a/docs/tutorial/06-advanced/01-in-depth/10-typescript.md b/docs/tutorial/06-advanced/01-in-depth/10-typescript.md
new file mode 100644
index 000000000..28e4e4753
--- /dev/null
+++ b/docs/tutorial/06-advanced/01-in-depth/10-typescript.md
@@ -0,0 +1,173 @@
+---
+title: Typescript
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In terms of Typescript, we have indeed spent a lot of effort on optimization in order to provide a better user experience. We try our best to use automatic type inference to reduce the trouble of defining types for you.
+
+## Automatically infer alova useHooks states type
+
+When createAlova creates an alova instance, the state types created by all useHooks such as `useRequest`, `useWatcher`, and `useFetcher` are automatically inferred based on the passed `statesHook`.
+
+The following are the status types returned by useHooks by default.
+
+
+
+
+```typescript
+const vueAlova = createAlova({
+ statesHook: VueHook
+ // ...
+});
+const {
+ loading, // Ref
+ data, // Ref<{ data: any }>
+ error // Ref
+} = useRequest(vueAlova.Get<{ data: any }>('/todo/list'));
+```
+
+
+
+
+```typescript
+const reactAlova = createAlova({
+ statesHook: ReactHook
+ // ...
+});
+const {
+ loading, // boolean
+ data, // { data: any }
+ error // Error
+} = useRequest(reactAlova.Get<{ data: any }>('/todo/list'));
+```
+
+
+
+
+```typescript
+const svelteAlova = createAlova({
+ statesHook: SvelteHook
+ // ...
+});
+const {
+ loading, // Writable
+ data, // Writable<{ data: any }>
+ error // Writable
+} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list'));
+```
+
+
+
+
+The type of data will be different depending on the response data type specified in different Method instances, let's continue to look below.
+
+## Type of response data
+
+When you specify a type for a data interface, you need to divide it into two situations.
+
+### Case 1
+
+When the response data does not need to be converted by calling `transform`, the type can be specified directly through generics.
+
+```typescript
+interface Todo {
+ title: string;
+ time: string;
+ done: boolean;
+}
+const Get = alovaInstance.Get('/todo/list');
+const { data } = useRequest(Get);
+// vue: The type of data is Ref
+// react: The type of data is Todo[]
+// svelte: The type of data is Writable
+```
+
+### Case 2
+
+When the response data needs to be converted by calling `transform`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type.
+
+```typescript
+interface Todo {
+ title: string;
+ time: string;
+ done: boolean;
+}
+const Get = alovaInstance.Get('/todo/list', {
+ //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type.
+ transform(data: Todo[], headers) {
+ return data.map(item => ({
+ ...item,
+ status: item.done ? 'Completed' : 'Not completed'
+ }));
+ }
+});
+
+const { data } = useRequest(Get);
+// vue: The type of data is Ref<(Todo & { status: string })[]>
+// react: The type of data is (Todo & { status: string })[]
+// svelte: The type of data is Writable<(Todo & { status: string })[]>
+```
+
+:::warning note
+
+The response data is converted by the global response interceptor, so when setting the type, it should also be set to the converted type.
+
+:::
+
+## Type inferred from request adapter
+
+Because alova supports custom request adapters, and the request configuration objects, response objects, and response headers of different adapters may be different, so the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created The types will be automatically inferred based on the types provided by the request adapter. Let's look at these types first.
+
+If you are using `alova/fetch`, alova will automatically infer the type using `fetch api`, The types of fetch api are as follows.
+
+```typescript
+declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise;
+```
+
+### Method configuration type of instance
+
+The method configuration type will be automatically inferred as:
+
+```typescript
+// AlovaMethodCommonConfig is a unified request parameter and behavior parameter
+// highlight-start
+const methodConfig: AlovaMethodCommonConfig & RequestInit = {
+ // highlight-end
+ // ...
+};
+alovaInstance.Get('/api/user', methodConfig);
+```
+
+### Global response interceptor parameter type
+
+The type of responded interceptor will be automatically inferred as:
+
+```typescript
+createAlova({
+ // ...
+ // highlight-start
+ responded: (response: Response, method: Method) => {
+ // highlight-end
+ // ...
+ }
+});
+```
+
+## Custom meta type
+
+By default, the type of method meta is `any`. When you need a standard format, you can configure the following type in the project's declaration file.
+
+```ts
+import 'alova';
+
+declare module 'alova' {
+ export interface AlovaCustomTypes {
+ meta: {
+ role: string;
+ errorModal: boolean;
+ };
+ }
+}
+```
diff --git a/docs/tutorial/06-advanced/01-in-depth/README.md b/docs/tutorial/06-advanced/01-in-depth/README.md
new file mode 100644
index 000000000..1f08bc4e7
--- /dev/null
+++ b/docs/tutorial/06-advanced/01-in-depth/README.md
@@ -0,0 +1,9 @@
+---
+title: Advanced
+---
+
+import DocCardList from '@theme/DocCardList';
+
+The advanced tutorial allows you to have a deeper understanding of some features of alova. They may not be commonly used functions, but can help you quickly solve more special request problems.
+
+
diff --git a/docs/tutorial/06-advanced/01-in-depth/_category_.json b/docs/tutorial/06-advanced/01-in-depth/_category_.json
new file mode 100644
index 000000000..41f002209
--- /dev/null
+++ b/docs/tutorial/06-advanced/01-in-depth/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "alova In-Depth"
+}
diff --git a/docs/tutorial/06-advanced/02-custom/01-http-adapter.md b/docs/tutorial/06-advanced/02-custom/01-http-adapter.md
new file mode 100644
index 000000000..7490a9216
--- /dev/null
+++ b/docs/tutorial/06-advanced/02-custom/01-http-adapter.md
@@ -0,0 +1,174 @@
+---
+title: Request Adapter
+---
+
+Remember how to create an Alova instance?
+
+```javascript
+import adapterFetch from 'alova/fetch';
+
+const alovaInstance = createAlova({
+ // ...
+ requestAdapter: adapterFetch()
+});
+```
+
+`requestAdapter` is the request adapter. The internal request sending and receiving will rely on the request adapter. `alova/fetch` manages requests through the fetch api. In most cases, we can use it. However, when `alova` runs in an environment where the fetch api is not available (such as app, mini program), you need to replace a request adapter that supports the current environment.
+
+So how should you customize a request adapter? It's very simple. It is actually a function that will be called every time a request is initiated and returns an object. This object contains request-related data sets such as `url`, `method`, `data`, `headers`, `timeout`, etc. Although there are many fields, we only need to access the data we need.
+
+## Request adapter structure
+
+The request adapter will receive request-related parameters and the method instance currently being requested, and return a response-related function set.
+
+```javascript
+function CustomRequestAdapter(requestElements, methodInstance) {
+ // Send the request here...
+
+ return {
+ async response() {
+ // Return response data
+ },
+ async headers() {
+ // Asynchronous function that returns the response header
+ },
+ abort() {
+ // Interrupt the request, this function will be triggered when abort is called externally
+ },
+ onDownload(updateDownloadProgress) {
+ // Download progress information, updateDownloadProgress is continuously called internally to update the download progress
+ },
+ onUpload(updateUploadProgress) {
+ // Upload progress information, updateUploadProgress is continuously called internally to update the upload progress
+ }
+ };
+}
+```
+
+### Request parameter details
+
+**requestElements**
+
+Send the relevant elements of the request, including the following data.
+
+```typescript
+interface RequestElements {
+ // Request url, get parameters are already included
+ readonly url: string;
+
+ // Request type, such as GET, POST, PUT, etc.
+ readonly type: MethodType;
+
+ // Request header information, object
+ readonly headers: Arg;
+
+ // Request body information
+ readonly data?: RequestBody;
+}
+```
+
+**methodInstance**
+
+The method instance of the current request
+
+### Return parameter details
+
+**response (required)**
+
+An asynchronous function, the function returns a response value, which will be passed to the global response interceptor (responded);
+
+**headers (required)**
+
+An asynchronous function, the response header object returned by the function will be passed to the transform of the Method instance Conversion hook function;
+
+**abort (required)**
+
+A normal function, which is used to interrupt the request. All interrupt requests will eventually call this function to execute;
+
+**onDownload (optional)**
+
+A normal function, which receives a callback function to update the download progress. The frequency of progress update is customized in this function. In this example, it is simulated to update every 100 milliseconds. The `updateDownloadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the downloaded size;
+
+**onUpload (optional)**
+
+A normal function, which receives a callback function to update the upload progress. The frequency of progress update is customized in this function. In this example, it is simulated to update every 100 milliseconds. The `updateUploadProgress` callback function receives two parameters, the first parameter is the total size, and the second parameter is the uploaded size;
+
+## XMLHttpRequest request adapter example
+
+The following is an example of an adapter that sends a request through XMLHttpRequest. It is mainly used to demonstrate the writing of the adapter. The code is incomplete and cannot be run.
+
+```javascript
+function XMLHttpRequestAdapter(requestElements, methodInstance) {
+ // Deconstruct the data needed
+ const { url, type, data, headers } = config;
+
+ // Send request
+ const xhr = new XMLHttpRequest();
+ xhr.open(type, url);
+ for (const key in headers) {
+ xhr.setRequestHeader(key, headers[key]);
+ }
+ const responsePromise = new Promise((resolve, reject) => {
+ xhr.addEventListener('load', event => {
+ // Process response data
+ resolve(/* ... */);
+ });
+ xhr.addEventListener('error', event => {
+ // Process request error
+ reject(/* ... */);
+ });
+ });
+
+ xhr.send(JSON.stringify(data));
+
+ return {
+ // Asynchronous function that returns response data
+ response: () => responsePromise,
+
+ // Asynchronous function that returns the response header
+ headers: () => responsePromise.then(() => xhr.getAllResponseHeaders()),
+ abort: () => {
+ xhr.abort();
+ },
+
+ // Download progress information, continuously call updateDownloadProgress internally to update the download progress
+ onDownload: updateDownloadProgress => {
+ xhr.addEventListener('progress', event => {
+ // Data receiving progress
+ updateDownloadProgress(event.total, event.loaded);
+ });
+ },
+
+ // Upload progress information, continuously call updateUploadProgress internally to update the upload progress
+ onUpload: updateUploadProgress => {
+ xhr.upload.onprogress = event => {
+ updateUploadProgress(event.total, event.loaded);
+ };
+ }
+ };
+}
+```
+
+:::note
+
+For more complete request adapter details, please refer to [alova/fetch Source code](https://github.com/alovajs/alova/blob/main/packages/alova/src/predefine/adapterFetch.ts) to understand.
+
+:::
+
+## Request adapter type
+
+The global `beforeRequest`, `responded` interceptors, and the types of configuration objects when creating method instances are automatically inferred based on the types provided by the request adapter. The following is the type of `alova/fetch`.
+
+```javascript
+import type { AlovaRequestAdapter } from 'alova';
+
+export type adapterFetch = () => AlovaRequestAdapter;
+```
+
+The generic parameters in `AlovaRequestAdapter` are values of three types: `RequestConfig`, `Response`, and `ResponseHeader`, which are automatically inferred to the types given by the request adapter in global interceptors, method instance configurations, etc.
+
+They are represented as follows:
+
+- **RequestConfig**: request configuration object type, which will be applied to the `config` parameter when the method is created.
+- **Response**: response type, for example, `alova/fetch` is the Response type
+- **ResponseHeader**: response header object type
diff --git a/docs/tutorial/06-advanced/02-custom/02-storage-adapter.md b/docs/tutorial/06-advanced/02-custom/02-storage-adapter.md
new file mode 100644
index 000000000..ce7be8b92
--- /dev/null
+++ b/docs/tutorial/06-advanced/02-custom/02-storage-adapter.md
@@ -0,0 +1,90 @@
+---
+title: Storage Adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+alova involves multiple features that require data persistence, such as persistent caching, silent submission, and offline submission. **By default, alova uses `localStorage` to store persistent data**, but considering the non-browser environment, it also supports customization.
+
+Customizing the storage adapter is also very simple. You only need to specify the functions for saving data, getting data, and removing data, which is roughly like this.
+
+
+
+
+```javascript
+const customStorageAdapter = {
+ set(key, value) {
+ // Save data, value is structured data, you can call JSON.stringify to convert it to a string
+ },
+ get(key) {
+ // Get data, you need to return structured data, you can call JSON.parse to convert it to an object
+ },
+ remove(key) {
+ // Remove data
+ },
+ clear() {
+ // Clear data
+ }
+};
+```
+
+Use a custom adapter.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ storageAdapter: customStorageAdapter
+});
+```
+
+
+
+
+```ts
+import { AlovaGlobalCacheAdapter } from 'alova';
+
+class CustomStorageAdapter implements AlovaGlobalCacheAdapter {
+ set(key, value) {
+ // Save data, value is structured data, you can call JSON.stringify to convert it to a string
+ }
+ get(key) {
+ // Get data, you need to return structured data, you can call JSON.parse to convert it to an object
+ }
+ remove(key) {
+ // Remove data
+ }
+ clear() {
+ // Clear data
+ }
+}
+```
+
+Use a custom adapter.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ storageAdapter: new CustomStorageAdapter()
+});
+```
+
+
+
+
+## SessionStorage Storage Adapter Example
+
+```javascript
+const sessionStorageAdapter = {
+ set(key, value) {
+ sessionStorage.setItem(key, JSON.stringify(value));
+ },
+ get(key) {
+ const data = sessionStorage.getItem(key);
+ return data ? JSON.parse(data) : data;
+ },
+ remove(key) {
+ sessionStorage.removeItem(key);
+ }
+};
+```
diff --git a/docs/tutorial/06-advanced/02-custom/03-client-strategy.md b/docs/tutorial/06-advanced/02-custom/03-client-strategy.md
new file mode 100644
index 000000000..ac8d58b04
--- /dev/null
+++ b/docs/tutorial/06-advanced/02-custom/03-client-strategy.md
@@ -0,0 +1,254 @@
+---
+title: Client Strategy
+---
+
+alova's client strategies are divided into three types: middleware, interceptor, and useHook. When your project needs customization, you can refer to this section.
+
+## Middleware
+
+Middleware provides powerful capabilities that can control almost all behaviors of a request. You can use it to control request behavior, customize request status, error handling, etc. For details, please go to [Request Middleware](/next/tutorial/client/in-depth/middleware) to view. The following source code can tell you what the middleware can do.
+
+- [actionDelegationMiddleware](https://github.com/alovajs/alova/blob/main/packages/client/src/middlewares/actionDelegation.ts) Cross-component triggering requests are implemented through middleware.
+- [useSQRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/silent/useSQRequest.ts) Implement immediate response requests in middleware without waiting.
+- [useSerialRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/serial/useSerialRequest.ts) Serialize requests in middleware and manage response data for multiple requests.
+- [useRetriableRequest](https://github.com/alovajs/alova/blob/main/packages/client/src/hooks/useRetriableRequest.ts) Retry failed requests in middleware.
+- [Delayed update loading](/next/tutorial/project/best-practice/middleware) example.
+
+## Interceptor
+
+Interceptors control global pre- and post-request behaviors. We can wrap global interceptors to implement interceptors with specific functions.
+
+The following is an example that interrupts all ongoing requests under certain conditions.
+
+```js
+const methodsAborter = (handler, detector) => {
+ let requestingMethods = [];
+ // Return the pre-request interceptor
+ return method => {
+ if (detector()) {
+ requestingMethods.forEach(method => {
+ method.abort();
+ });
+ return;
+ }
+ requestingMethods.push(method);
+ method.promise
+ ?.then(() => {
+ requestingMethods = requestingMethods.filter(item => item !== method);
+ })
+ .catch(() => {}); // Prevent errors from being thrown
+ handler(method);
+ };
+};
+
+createAlova({
+ beforeRequest: methodsAborter(
+ method => {
+ // Original pre-request hook
+ },
+ () => {
+ // Interrupt request judgment condition
+ return false;
+ }
+ )
+});
+```
+
+A more complex example, [token Authentication interceptor](https://github.com/alovajs/alova/blob/main/packages/client/src/functions/tokenAuthentication/createTokenAuthentication.ts)
+
+## useHook
+
+useHook is the most commonly used request strategy of alova, and it is cross-UI framework. When you write useHook for a specific UI framework, you can write it just like ordinary useHook. Here we mainly understand the useHook writing across UI frameworks.
+
+We innovatively use **state proxy** to smooth out the differences in UI framework states. Its usage is similar to Vue's ref value. You only need to simply access the v attribute or assign a value to the v attribute, and you no longer need to care about the differences in UI frameworks.
+
+In order to smooth out the differences in state proxies, we provide the `statesHookHelper` function to create auxiliary functions, and use these auxiliary functions to implement useHook across UI frameworks.
+
+```js
+import { statesHookHelper } from '@alova/shared/function';
+import { promiseStatesHook } from 'alova';
+
+function myUseHook(methodHandler, options) {
+ const {
+ create,
+ computed,
+ ref,
+ onMounted,
+ onUnmount,
+ watch,
+ objectify,
+ exposeProvider,
+ __referingObj
+ } = statesHookHelper(promiseStatesHook());
+}
+```
+
+Next, let's take a closer look at them.
+
+### Create a state
+
+Use `create` to create a UI framework-independent state proxy FrameworkState, taking the loading state as an example.
+
+```js
+// Parameter 1: initial value
+// Parameter 2: export key
+const loading = create(false, 'loading');
+
+// Get the original value
+const dehydratedLoading = loading.v;
+// Update the state value
+loading.v = true;
+// Get the export value, call `statesHook.export` to export the value internally
+const exportedLoading = loading.e;
+// Get the platform-related state value created by statesHook.create
+const platformedState = loading.s;
+```
+
+### Create a computed value
+
+Use `computed` to create a UI framework-independent computed value. In order to be compatible with react, you need to pass in the computed value dependency, which can be a platform-related state value and FrameworkState.
+
+```js
+// Parameter 1: Function of computed value
+// Parameter 2: Computed property dependency
+// Parameter 3: Export key
+const computedState = computed(() => !loading.v, [loading], 'states');
+
+// Get the original value
+const dehydratedComputedState = computedState.v;
+// Get the exported value, call `statesHook.export` to export the value internally
+const exportedComputedState = computedState.e;
+// Get the platform-related state value created by statesHook.computed
+const platformedComputedState = computedState.s;
+```
+
+### Create reference value
+
+It is mainly used to cross the closure trap of react, call `useRef` internally, and other frameworks directly return `{ current: value }` objects, which are accessed and updated through `unifiedValue.current`.
+
+```js
+const unifiedValue = ref({});
+```
+
+### Component mounting hook
+
+```js
+onMounted(() => {});
+```
+
+### Component uninstallation hook
+
+```js
+onUnmount(() => {});
+```
+
+### Monitor state changes
+
+```js
+// Parameter 1: Monitor item, used for compatibility with react, can pass platform-related state values and FrameworkState
+// Parameter 2: Callback function
+watch([loading, computedState.e], () => {});
+```
+
+### State objectification
+
+Convert the state proxy array unrelated to the UI framework into a state object, generally used with `exposeProvider`, with key as the key, and the second parameter can also specify the value of the object.
+
+```js
+const states = objectify([loading, data, error]);
+/* The value of states is
+{
+loading: loading,
+data: data,
+error: error
+}
+*/
+
+const states = objectify([loading, data, error], 's');
+/* The value of states is
+{
+loading: loading.s,
+data: data.s,
+error: error.s
+}
+*/
+```
+
+### Expose internal data
+
+If the internal information of useHook is exported directly, it will become unusable, so `exposeProvider` is provided for export, which will automatically help us handle the following:
+
+1. Automatically convert the state proxy to the state of the corresponding UI framework.
+2. Provide a unified update function. If the incoming parameters include the states and update functions returned by useRequest, they will also be automatically compatible with these.
+3. In react, functions that do not start with on are wrapped with memorize, and those that have been wrapped are no longer wrapped.
+4. Functions that start with on are considered event binding functions and will be directly added to the export object.
+5. Export a unified referringObject.
+
+The following is an export example of `usePagination`:
+
+```js
+export const usePagination = (/* ... */) => {
+ return exposeProvider({
+ // Return object of useWatcher in current useHook
+ ...useWatcherReturns,
+
+ // State array to object
+ ...objectify([page, pageSize, data, pageCount, total, isLastPage]),
+
+ // Operation function
+ reset: () => {
+ // ...
+ },
+
+ // Event binding function
+ onFetchSuccess: fetchState.onSuccess
+ // ...
+ });
+};
+```
+
+### `__referingObj` Description
+
+`__referingObj` is for compatibility with options style and class style UI frameworks. It is a common reference object used to synchronize the component objects of options and class style, so that the corresponding component objects can be accessed in statesHook. The same referingObj object needs to be used in the custom useHook to ensure that the states in a useHook can access the same component object.
+`__referingObj` will be created and returned in `statesHookHelper`, and no specific processing is required. Just export it as follows.
+
+When the alova core hook is used in the scene hook, pass this object into the hook and ensure that it is exported in this useHook.
+
+> If referingObj is not passed in, the core hook will be automatically created internally
+
+```js
+
+export const useXXX = (...) => {
+const {__referingObj,
+// ...
+} = statesHookHelper(promiseStatesHook());
+
+const states = useReqest(methodHandler, {
+__referingObj
+});
+
+return {
+// ...
+__referingObj,
+}
+}
+```
+
+When the alova core hook is not used in the scene hook, the referingObj object is exported directly in this useHook.
+
+```js
+export const useXXX = (/* ... */) => {
+ const {
+ __referingObj
+ // ...
+ } = statesHookHelper(promiseStatesHook());
+ // ...
+
+ return {
+ // ...
+ __referingObj
+ };
+};
+```
+
+When you use `exposeProvider` to export information, it will automatically export `__referingObj` without us having to handle it manually.
diff --git a/docs/tutorial/06-advanced/02-custom/04-server-strategy.md b/docs/tutorial/06-advanced/02-custom/04-server-strategy.md
new file mode 100644
index 000000000..fea23a344
--- /dev/null
+++ b/docs/tutorial/06-advanced/02-custom/04-server-strategy.md
@@ -0,0 +1,44 @@
+---
+title: Server Strategy
+---
+
+Server strategy is also called `Server hook`, which is a decorated function of a method instance.
+
+The following is the specification of server hooks, which receives a method instance and returns a new method instance, so it is very convenient to combine multiple server hooks.
+
+```ts
+export interface AlovaServerHook> {
+ (method: Method, options: Options): Method;
+}
+```
+
+Customizing `Server hook` is very simple. The following is a simple request retry example.
+
+```js
+import { HookedMethod } from 'alova/server';
+
+const retry = (method, options) => {
+ const { retry: maxRetryTimes = 3, delay = 1000 } = options;
+ let retryTimes = 0;
+
+ return new HookedMethod(method, forceRequest =>
+ method.send(forceRequest).then(
+ value => value,
+ error => {
+ if (retryTimes < maxRetryTimes) {
+ retryTimes += 1;
+ setTimeout(() => {
+ method.send(forceRequest).catch(noop);
+ }, delay);
+ }
+ }
+ )
+ );
+};
+
+// Usage
+const userData = await retry(alova.Get('/api/user'), {
+ retry: 5,
+ delay: 1500
+});
+```
diff --git a/docs/tutorial/06-advanced/02-custom/05-stateshook.md b/docs/tutorial/06-advanced/02-custom/05-stateshook.md
new file mode 100644
index 000000000..33b7e4a24
--- /dev/null
+++ b/docs/tutorial/06-advanced/02-custom/05-stateshook.md
@@ -0,0 +1,121 @@
+---
+title: States Hook
+---
+
+Remember how to create an Alova instance?
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ statesHook: ReactHook
+});
+```
+
+`statesHook` will determine which UI library state is returned when requested. In most cases you don't need to customize `statesHook`, but if you need to adapt more MVVM libraries that alova does not support, you need to customize `statesHook`.
+
+`statesHook` is a normal object containing specific functions. Let's take a look at how **VueHook** is written.
+
+## statesHook structure
+
+statesHook is an object. The following is its type definition.
+
+```ts
+interface StatesHook {
+ /**
+ * Create state
+ * @param initialValue Initial data
+ * @returns State value
+ */
+ create: (initialValue: any, referingObject: ReferingObject) => State;
+
+ /**
+ * Create computed state
+ * @param initialValue Initial data
+ * @param referingObject Reference object
+ */
+ computed: (getter: () => any, deps: Export[], referingObject: ReferingObject) => Computed;
+
+ /**
+ * Export value for developers
+ * @param state State value
+ * @param referingObject refering object
+ * @returns Exported value
+ */
+ export?: (state: State, referingObject: ReferingObject) => Export;
+
+ /** Convert state to normal data */
+ dehydrate: (state: State, key: string, referingObject: ReferingObject) => any;
+
+ /**
+ * Update state value
+ * @param newVal new data set
+ * @param state original state value
+ * @param @param referingObject refering object
+ */
+ update: (newVal: any, state: State, key: string, referingObject: ReferingObject) => void;
+
+ /**
+ * Control the function of executing the request. This function will be executed once when useRequest and useWatcher are called
+ * Executed once in the fetch function in useFetcher
+ * When watchingStates is an empty array, execute the handleRequest function once
+ * When watchingStates is a non-empty array, call when the state changes. When immediate is true, call it immediately
+ * hook is an instance of use hook. Each time use hook is called, a hook instance will be generated
+ * It can be executed directly in vue, but it needs to be executed in useEffect in react
+ * removeStates function is a function to clear the current state. It should be called when the component is uninstalled
+ */
+ effectRequest: (
+ effectParams: EffectRequestParams,
+ referingObject: ReferingObject
+ ) => void;
+
+ /**
+ * Wrap send, abort and other use hooks operation functions
+ * This is mainly used to optimize the problem of generating new functions for each rendering in react and optimize performance
+ * @param fn use hook operation function
+ * @returns wrapped operation function
+ */
+ memorize?: any>(fn: Callback) => Callback;
+
+ /**
+ * Create a reference object
+ * @param initialValue initial value
+ * @returns reference object containing initial value
+ */
+ ref?: (initialValue: D) => { current: D };
+
+ /**
+ * Status monitoring
+ * @param source monitoring status
+ * @param callback status change callback function
+ * @param referingObject referring object
+ */
+ watch: (source: Watched[], callback: () => void, referingObject: ReferingObject) => void;
+
+ /**
+ * Component mounting hook
+ * @param callback callback function
+ * @param referingObject refering object
+ */
+ onMounted: (callback: () => void, referingObject: ReferingObject) => void;
+
+ /**
+ * Component uninstallation hook
+ * @param callback callback function
+ * @param referingObject refering object
+ */
+ onUnmounted: (callback: () => void, referingObject: ReferingObject) => void;
+}
+```
+
+:::warning Note
+
+If statesHook involves something similar to `react`, when `alova`'s use hook is called every time it is re-rendered, the `saveStates` function needs to be triggered every time it is re-rendered in `effectRequest`, because `react` refreshes its state references every time it is re-rendered, so we need to re-save them again.
+
+:::
+
+The following is the statesHook source code that leaves the UI framework.
+
+- [react hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/react.ts)
+- [vue hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/vue.ts)
+- [svelte hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/svelte.ts)
+- [vue options hook](https://github.com/alovajs/alova/blob/main/packages/vue-options/src/stateHook.ts)
diff --git a/docs/tutorial/06-advanced/02-custom/README.md b/docs/tutorial/06-advanced/02-custom/README.md
new file mode 100644
index 000000000..3e24ff75b
--- /dev/null
+++ b/docs/tutorial/06-advanced/02-custom/README.md
@@ -0,0 +1,31 @@
+---
+title: Overview
+---
+
+alova has high scalability. In addition to providing common features such as core caching mechanism, request sharing mechanism and state management, it also provides various customization functions and middleware mechanisms, which can adapt to different js environments and customize request strategies. In the following chapters, we will introduce them in detail.
+
+## Custom Adapter
+
+In order to meet the running requirements of js in different environments, you can customize the request adapter, storage adapter, and even the state adapter of the UI framework, which will also be introduced in detail in the following chapters. Some adapter examples are listed below.
+
+- [fetch adapter](https://github.com/alovajs/alova/blob/main/packages/alova/src/predefine/adapterFetch.ts)
+- [localStorage storage adapter](https://github.com/alovajs/alova/blob/main/packages/alova/src/defaults/cacheAdapter.ts)
+- [vue states hook](https://github.com/alovajs/alova/blob/main/packages/client/src/statesHook/vue.ts)
+
+You can also group multiple types of adapters into a collection, such as [Uniapp adapter](/next/resource/request-adapter/alova-adapter-uniapp).
+
+## Custom client strategy
+
+alova provides 10+ custom client strategy modules, but sometimes you may need to write your own strategy module. Usually, a custom request strategy is based on the combination of the three core useHooks of `useRequest`, `useWatcher` and `useFetcher`, and writes [middleware](/next/tutorial/client/in-depth/middleware) and cache manipulation functions for them to control their request methods, so as to achieve various request strategies
+
+The following strategy modules are very representative, and it is strongly recommended that you refer to the source code for inspiration.
+
+- [source of usePagination](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/pagination/usePagination.ts)
+- [source of useCaptcha](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/useCaptcha.ts)
+- [source of useForm](https://github.com/alovajs/scene/blob/main/packages/client/src/hooks/useForm.ts)
+
+## Custom server-side strategy
+
+The server-side strategy module is a simple function. The following is a `Server hook` for request retry.
+
+- [Request retry](https://github.com/alovajs/scene/blob/main/packages/server/src/hooks/retry.ts)
diff --git a/docs/tutorial/06-advanced/02-custom/_category_.json b/docs/tutorial/06-advanced/02-custom/_category_.json
new file mode 100644
index 000000000..0b24b5159
--- /dev/null
+++ b/docs/tutorial/06-advanced/02-custom/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Custom"
+}
diff --git a/docs/tutorial/06-advanced/_category_.json b/docs/tutorial/06-advanced/_category_.json
index 3462df72e..e29c61856 100644
--- a/docs/tutorial/06-advanced/_category_.json
+++ b/docs/tutorial/06-advanced/_category_.json
@@ -1,3 +1,5 @@
-{
- "label": "Advanced"
-}
+{
+ "label": "Advanced",
+ "collapsed": false,
+ "collapsible": false
+}
diff --git a/docs/tutorial/07-project/01-best-practice/01-manage-apis.md b/docs/tutorial/07-project/01-best-practice/01-manage-apis.md
new file mode 100644
index 000000000..94899f868
--- /dev/null
+++ b/docs/tutorial/07-project/01-best-practice/01-manage-apis.md
@@ -0,0 +1,153 @@
+---
+title: Manage APIs
+---
+
+In a project, we may need to use hundreds or thousands of request APIs, so managing these request APIs becomes particularly important.
+
+You may write the request code like the code snippet in [quick start](/next/tutorial/getting-started/quick-start). all codes in one file.
+
+```javascript
+const { loading, data, error } = useRequest(
+ alovaInstance.Get('https://api.alovajs.org/profile', {
+ params: {
+ id: 1
+ }
+ })
+);
+```
+
+This is just for beginners to understand, but in actual projects, we do not recommend this, because the method instance is not only used to send requests, it can also be used to operate cache and state, the above usage will make these request api become It's unmanageable, and if you think it's wrong, you might forget a little:
+
+> The key of the response data cache is uniquely identified by the combination of the method instance’s request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody). Or different positions will be treated as different keys.
+
+Therefore, in actual projects, method instances should be managed, and alova instances can also be managed uniformly.
+
+## api file structure
+
+First of all, your project needs a folder that uniformly stores method instances and alova instances, for example called `api`, the following is a common api management structure, and you can also use any structure suitable for the project.
+
+```
+|-api
+| |-index.js -> contains all alova instances
+| |-methods
+| | |-user.js
+| | |-article.js
+| | |-order.js
+| | |-...
+|-...
+```
+
+In short, your project should use a suitable folder structure to organize them.
+
+> Next, take vue as an example to show the sample code
+
+## Manage alova instance
+
+Your project may need to communicate with different servers, or you may need to use special request schemes in specific requests, or use different response interceptors, etc. All of these require creating and maintaining multiple alova instances in the project. It is recommended to Use a separate file to manage them, for example in the above api management structure, will use `api/index.js` to manage.
+
+```javascript title=api/index.js
+import { createAlova } from 'alova';
+import VueHook from 'alova/vue';
+import adapterFetch from 'alova/fetch';
+import { axiosRequestAdapter } from '@alova/adapter-axios';
+
+// user alova instance
+export const userAlova = createAlova({
+ baseURL: 'https://api-user.alovajs.org',
+ statesHook: VueHook,
+ requestAdapter: adapterFetch(),
+ async responded(method) {
+ method.config.headers.token = 'user token';
+ }
+});
+
+// order alova instance
+export const orderAlova = createAlova({
+ baseURL: 'https://api-order.alovajs.org',
+ statesHook: VueHook,
+ requestAdapter: adapterFetch(),
+ async responded(method) {
+ method.config.headers.token = 'order token';
+ }
+});
+
+// upload alova instance
+export const uploadAlova = createAlova({
+ baseURL: 'https://api-order.alovajs.org',
+ statesHook: VueHook,
+ requestAdapter: axiosRequestAdapter()
+});
+```
+
+## Manage method instances
+
+We can use different js files to classify and manage method instances. For example, in the above api management structure, `api/methods/user.js` will be used to manage method instances related to user information, and `api/methods/order.js` will be used `A method instance related to order management.
+
+In addition, as mentioned above, in addition to sending requests, method instances can also be used to operate caches and states. In order to ensure the number and order of request parameters, we can use a function to correspond to a request API, through The corresponding method instance is returned in the form of incoming request parameters. As long as the incoming parameters are the same, the request information and parameter order of the method instance are also the same, so as to ensure that the method instance used to operate the cache and state is correct.
+
+```javascript title=api/methods/user.js
+import { userAlova } from '..';
+
+// Get user information
+export const getUserInfo = id => userAlova.Get('/user/' + id);
+
+// Edit user information
+export const editUserInfo = (name, age, mobile) =>
+ userAlova.Post('/user', {
+ name,
+ age,
+ mobile
+ });
+
+// remove user
+export const removeUser = id => userAlova.Delete('/user/' + id);
+
+//...
+```
+
+In the **user component**, the method function can be directly imported for use, and the method function can be used again to invalidate the corresponding cache after calling `invalidateCache`.
+
+```html title=views/user.vue
+
+
+
+ );
+}
diff --git a/src/pages/_indexComponent/Features/index.tsx b/src/pages/_indexComponent/Features/index.tsx
index 9e1eb3314..1d83740e4 100644
--- a/src/pages/_indexComponent/Features/index.tsx
+++ b/src/pages/_indexComponent/Features/index.tsx
@@ -1,86 +1,94 @@
-import Translate from '@docusaurus/Translate';
-import IconFont from '@site/src/components/IconFont';
-import clsx from 'clsx';
-import React from 'react';
-import styles from './styles.module.css';
-
-type FeatureItem = {
- title: JSX.Element;
- icon: string;
- description: JSX.Element;
-};
-
-const FeatureList: FeatureItem[] = [
- {
- title: (
- High performance request strategy
- ),
- icon: 'shandian',
- description: (
-
- 10+ request strategy modules that can be used directly, just choose the one you want to use, and it can also
- reduce performance problems caused by requests.
-
- )
- },
- {
- title: Simple and familiar,
- icon: 'check',
- description: (
-
- API design similar to axios, making it easier and more familiar for you to get started
-
- )
- },
- {
- title: Request-level cache,
- icon: 'shujuku',
- description: (
-
- Provides various server-side data cache modes such as memory mode and persistence mode to improve user
- experience and reduce server pressure
-
- )
- },
- {
- title: Lightweight,
- icon: 'box',
- description: (
- compressed version is only 4kb+, only 30% of axios
- )
- }
-];
-
-function Feature({ title, icon, description }: FeatureItem) {
- return (
-
-
- );
-}
+import Translate from '@docusaurus/Translate';
+import IconFont from '@site/src/components/IconFont';
+import clsx from 'clsx';
+import React from 'react';
+import styles from './styles.module.css';
+
+type FeatureItem = {
+ title: JSX.Element;
+ icon: string;
+ description: JSX.Element;
+};
+
+const FeatureList: FeatureItem[] = [
+ {
+ title: (
+
+ High performance request strategy
+
+ ),
+ icon: 'shandian',
+ description: (
+
+ 10+ request strategy modules that can be used directly, just choose the one you want to
+ use, and it can also reduce performance problems caused by requests.
+
+ )
+ },
+ {
+ title: (
+ Simple and familiar
+ ),
+ icon: 'check',
+ description: (
+
+ API design similar to axios, making it easier and more familiar for you to get started
+
+ )
+ },
+ {
+ title: (
+ Request-level cache
+ ),
+ icon: 'shujuku',
+ description: (
+
+ Provides various server-side data cache modes such as memory mode and persistence mode
+ to improve user experience and reduce server pressure
+
+ )
+ },
+ {
+ title: Lightweight,
+ icon: 'box',
+ description: (
+
+ compressed version is only 4kb+, only 30% of axios
+
+ )
+ }
+];
+
+function Feature({ title, icon, description }: FeatureItem) {
+ return (
+
-
- One line of code completes network requests in various complex scenarios. Don’t spend time on the small
- matter of requesting. Leave it to us.
-
-
- “
-
- Just like their weapon arsenal, alova grants them enhanced capabilities. Whether you prefer using axios,
- superagent, or the browser's fetch API, alova seamlessly integrates with all of them.
-
- ”
-
-
-
-
-
-
-
-
- );
-}
+import Link from '@docusaurus/Link';
+import Translate, { translate } from '@docusaurus/Translate';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import PageModule from '@site/src/components/PageModule';
+import CodeBlock from '@theme/CodeBlock';
+import Layout from '@theme/Layout';
+import clsx from 'clsx';
+import IconFont from '../components/IconFont';
+import Contributors from './_indexComponent/Contributors';
+import Features from './_indexComponent/Features';
+import Like from './_indexComponent/Like';
+import Strategy from './_indexComponent/Strategy';
+import Support from './_indexComponent/Support';
+import styles from './_indexComponent/index.module.css';
+
+function HomepageHeader() {
+ const buttons = [
+ {
+ text: (
+
+
+ One line of code completes network requests in various complex scenarios. Don’t
+ spend time on the small matter of requesting. Leave it to us.
+
+
+ “
+
+ Just like their weapon arsenal, alova grants them enhanced capabilities. Whether
+ you prefer using axios, superagent, or the browser's fetch API, alova seamlessly
+ integrates with all of them.
+
+ ”
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/theme/AnnouncementBar/Content/index.js b/src/theme/AnnouncementBar/Content/index.js
index a19ade10c..4d1c0dd38 100644
--- a/src/theme/AnnouncementBar/Content/index.js
+++ b/src/theme/AnnouncementBar/Content/index.js
@@ -1,26 +1,38 @@
+import Link from '@docusaurus/Link';
import Translate from '@docusaurus/Translate';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
-import pngLive from '@site/static/img/live.jpg';
import clsx from 'clsx';
-import styles from './styles.module.css';
+import styles from './styles.module.scss';
export default function AnnouncementBarContent(props) {
const { i18n } = useDocusaurusContext();
i18n.currentLocale;
return (
+ // Developer provided the HTML, so assume it's safe.
+ // eslint-disable-next-line react/no-danger
- ⭐️
-
- If you also like alova, star it on GitHub!
-
- ⭐️
+ className={clsx(styles.content, props.className, 'flex-col align-center')}>
+
+ alova v3.0.0-beta is HERE!
+
+
+
+ More simple, more powerful, support both client & server
+
+
+
-
-
+
+
diff --git a/static/iconfont/iconfont.css b/static/iconfont/iconfont.css
index 99ee8ba4b..2862f7186 100644
--- a/static/iconfont/iconfont.css
+++ b/static/iconfont/iconfont.css
@@ -1,12 +1,12 @@
@font-face {
- font-family: "iconfont"; /* Project id 4123843 */
+ font-family: 'iconfont'; /* Project id 4123843 */
src: url('iconfont.woff2?t=1710246562126') format('woff2'),
- url('iconfont.woff?t=1710246562126') format('woff'),
- url('iconfont.ttf?t=1710246562126') format('truetype');
+ url('iconfont.woff?t=1710246562126') format('woff'),
+ url('iconfont.ttf?t=1710246562126') format('truetype');
}
.iconfont {
- font-family: "iconfont" !important;
+ font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
@@ -14,54 +14,53 @@
}
.icon-kaishi:before {
- content: "\e63e";
+ content: '\e63e';
}
.icon-weixin:before {
- content: "\e637";
+ content: '\e637';
}
.icon-aixin:before {
- content: "\e85c";
+ content: '\e85c';
}
.icon-discord:before {
- content: "\ebf8";
+ content: '\ebf8';
}
.icon-a-logoline1:before {
- content: "\e62b";
+ content: '\e62b';
}
.icon-a-logoline:before {
- content: "\e62c";
+ content: '\e62c';
}
.icon-plus:before {
- content: "\e62d";
+ content: '\e62d';
}
.icon-uf_check:before {
- content: "\e626";
+ content: '\e626';
}
.icon-youjiantou:before {
- content: "\e62a";
+ content: '\e62a';
}
.icon-shandian:before {
- content: "\e628";
+ content: '\e628';
}
.icon-shujuku:before {
- content: "\e629";
+ content: '\e629';
}
.icon-check:before {
- content: "\e73c";
+ content: '\e73c';
}
.icon-box:before {
- content: "\e8c6";
+ content: '\e8c6';
}
-
diff --git a/static/iconfont/iconfont.js b/static/iconfont/iconfont.js
index 2a6fdd6a6..ef4982f8f 100644
--- a/static/iconfont/iconfont.js
+++ b/static/iconfont/iconfont.js
@@ -1 +1,66 @@
-window._iconfont_svg_string_4123843='',function(e){var t=(t=document.getElementsByTagName("script"))[t.length-1],l=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var c,o,i,n,a,s=function(t,l){l.parentNode.insertBefore(t,l)};if(l&&!e.__iconfont__svg__cssinject__){e.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}c=function(){var t,l=document.createElement("div");l.innerHTML=e._iconfont_svg_string_4123843,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(t=document.body).firstChild?s(l,t.firstChild):t.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(c,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),c()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=c,n=e.document,a=!1,h(),n.onreadystatechange=function(){"complete"==n.readyState&&(n.onreadystatechange=null,d())})}function d(){a||(a=!0,i())}function h(){try{n.documentElement.doScroll("left")}catch(t){return void setTimeout(h,50)}d()}}(window);
\ No newline at end of file
+(window._iconfont_svg_string_4123843 =
+ ''),
+ (function (e) {
+ var t = (t = document.getElementsByTagName('script'))[t.length - 1],
+ l = t.getAttribute('data-injectcss'),
+ t = t.getAttribute('data-disable-injectsvg');
+ if (!t) {
+ var c,
+ o,
+ i,
+ n,
+ a,
+ s = function (t, l) {
+ l.parentNode.insertBefore(t, l);
+ };
+ if (l && !e.__iconfont__svg__cssinject__) {
+ e.__iconfont__svg__cssinject__ = !0;
+ try {
+ document.write(
+ ''
+ );
+ } catch (t) {
+ console && console.log(t);
+ }
+ }
+ (c = function () {
+ var t,
+ l = document.createElement('div');
+ (l.innerHTML = e._iconfont_svg_string_4123843),
+ (l = l.getElementsByTagName('svg')[0]) &&
+ (l.setAttribute('aria-hidden', 'true'),
+ (l.style.position = 'absolute'),
+ (l.style.width = 0),
+ (l.style.height = 0),
+ (l.style.overflow = 'hidden'),
+ (l = l),
+ (t = document.body).firstChild ? s(l, t.firstChild) : t.appendChild(l));
+ }),
+ document.addEventListener
+ ? ~['complete', 'loaded', 'interactive'].indexOf(document.readyState)
+ ? setTimeout(c, 0)
+ : ((o = function () {
+ document.removeEventListener('DOMContentLoaded', o, !1), c();
+ }),
+ document.addEventListener('DOMContentLoaded', o, !1))
+ : document.attachEvent &&
+ ((i = c),
+ (n = e.document),
+ (a = !1),
+ h(),
+ (n.onreadystatechange = function () {
+ 'complete' == n.readyState && ((n.onreadystatechange = null), d());
+ }));
+ }
+ function d() {
+ a || ((a = !0), i());
+ }
+ function h() {
+ try {
+ n.documentElement.doScroll('left');
+ } catch (t) {
+ return void setTimeout(h, 50);
+ }
+ d();
+ }
+ })(window);
diff --git a/static/img/announcement_bg.png b/static/img/announcement_bg.png
new file mode 100644
index 000000000..6f81f963f
Binary files /dev/null and b/static/img/announcement_bg.png differ
diff --git a/static/img/overview_flow_cn.png b/static/img/overview_flow_cn.png
new file mode 100644
index 000000000..243065af1
Binary files /dev/null and b/static/img/overview_flow_cn.png differ
diff --git a/static/img/overview_flow_en.png b/static/img/overview_flow_en.png
new file mode 100644
index 000000000..be6a887f5
Binary files /dev/null and b/static/img/overview_flow_en.png differ
diff --git a/tsconfig.json b/tsconfig.json
index 3c2167899..314eab8a4 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,7 @@
-{
- // This file is not used in compilation. It is here just for a nice editor experience.
- "extends": "@docusaurus/tsconfig",
- "compilerOptions": {
- "baseUrl": "."
- }
-}
+{
+ // This file is not used in compilation. It is here just for a nice editor experience.
+ "extends": "@docusaurus/tsconfig",
+ "compilerOptions": {
+ "baseUrl": "."
+ }
+}
diff --git a/versioned_docs/version-2.x/about/01-RSM.md b/versioned_docs/version-2.x/about/01-RSM.md
new file mode 100644
index 000000000..63820c100
--- /dev/null
+++ b/versioned_docs/version-2.x/about/01-RSM.md
@@ -0,0 +1,60 @@
+---
+title: Request Scene Model (RSM)
+---
+
+## What is the request scene model
+
+The request scenario model is based on the perspective of the client. It describes the abstract model of the client from triggering the request intent to receiving the request result. It consists of four stages: request timing, request behavior, request event, and response management. For example, when making a request, you often need to think about the following questions,
+
+1. When the request is made;
+2. Whether to display the request status;
+3. Do you need to retry the request on failure;
+4. How to process the response data;
+5. Do you need to encrypt the request parameters;
+6. Whether to cache the frequently used response data;
+7. How to operate data across pages;
+8. How to process requests in a weak or disconnected network environment;
+9. ...
+
+`fetch` or `axios` are often more focused on how to interact with the server, but we always need to deal with the above problems by ourselves. These functions that are beneficial to application performance and stability will always allow programmers to write low-maintenance sexual code. The request scene model is to abstract all links from preparing the request to processing the response data, so as to cover the model of the entire CS interaction life cycle from the perspective of the front end. `alova` is a library that requests scene models, it is a supplement to request libraries such as `axios`, not a substitute.
+
+> CS interaction: generally refers to data interaction between all client types and servers
+
+## Request scene model
+
+![RSM](/img/rsm-en.png)
+
+## request timing
+
+Describe when a request needs to be made, implemented with `useHook` in `alova`.
+
+- Initialize display data, such as just entering a certain interface or sub-interface;
+- Human-computer interaction triggers CS interaction, and the request needs to be changed again, such as page turning, filtering, sorting, fuzzy search, etc.;
+- Send requests in an anti-shake manner, avoid view data flickering, and reduce server pressure
+- Preloading data, such as preloading the content of the next page in a page, predicting that the user clicks a button to pre-fetch data;
+- To operate server data, it is necessary to issue a request for addition, deletion, modification and query, such as submitting data, deleting data, etc.;
+- Synchronize server status, such as polling requests in scenarios where data changes rapidly, and re-pull data after operating a certain data;
+
+## Request Behavior
+
+Describes how to process the request, implemented as a Method abstraction in `alova`.
+
+- Placeholder request, when requesting, display loading, skeleton diagram, or real data used last time;
+- Cache high-frequency responses, multiple execution requests will use fresh data;
+- Multi-request serial and parallel;
+- The retry mechanism of important interfaces reduces the probability of request failure caused by network instability;
+- Submit silently. When you only care about submitting data, directly respond to the success event after submitting the request, and the background guarantees that the request is successful;
+- Offline submission, the submitted data will be temporarily stored locally when offline, and then submitted after the network connection;
+
+## request event
+
+Indicates sending a request with request parameters and getting a response. `alova` can work with any request library or native solution such as `axios`, `fetch`, `XMLHttpRequest`.
+
+## Response management
+
+`alova` makes the response data stateful and manages it in a unified manner, refreshes the view data and operates the cache at the request level, avoids operations at the component level, and is more elegant and unified.
+
+- Remove the cached response data, which will be pulled from the server when the request is made again;
+- Update the cached response data, which can update the response data at any location, which is very helpful for updating data across pages;
+- Refresh the response data, which can re-refresh the response data at any position, and is also very helpful for updating data across pages;
+- Customize the cache setting. When requesting batch data, you can manually set the cache for the batch data one by one, so as to meet the cache hit of subsequent single data;
diff --git a/docs/tutorial/11-others/02-comparison.md b/versioned_docs/version-2.x/about/02-comparison.md
similarity index 97%
rename from docs/tutorial/11-others/02-comparison.md
rename to versioned_docs/version-2.x/about/02-comparison.md
index c7099fc71..1c4557231 100644
--- a/docs/tutorial/11-others/02-comparison.md
+++ b/versioned_docs/version-2.x/about/02-comparison.md
@@ -1,93 +1,92 @@
----
-title: Compare with other libraries
-sidebar_position: 20
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-## Compare with axios
-
-axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library.
-
-But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios.
-
-### alova provides automated request status management for axios
-
-When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook.
-
-
-
-
-```javascript
-// vue3 example
-const loading = ref(false);
-const data = ref({});
-const error = ref(null);
-const request = async () => {
- try {
- loading.value = true;
- data.value = await axios.get('/xxx');
- } catch (e) {
- error.value = e;
- }
- loading.value = false;
-};
-mounted(request);
-```
-
-
-
-
-```javascript
-// Use axios as alova's request adapter
-const { loading, data, error } = useRequest(alova.Get('/xxx'));
-```
-
-
-
-
-### alova provides high-performance request strategies out of the box
-
-alova provides you with [multiple high-performance request strategy modules](/tutorial/strategy). You can use different modules according to different request scenarios, which axios does not have.
-
-### alova provides response data cache for axios
-
-alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure.
-
-### alova provides request sharing function for axios
-
-Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure.
-
-### alova provides data pre-fetching for axios
-
-Requesting the data to be used in advance can also greatly improve application fluency.
-
-### alova can manage request states
-
-You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication.
-
-## Compared with react-query and swr
-
-react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at.
-
-### alova has different goals
-
-In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved.
-
-### Method proxy design
-
-Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits:
-
-1. Unified usage without different usage depending on the platform or UI framework.
-2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration.
-3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs.
-4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache.
-
-### High flexibility
-
-Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios.
-
-### Lightweight
-
-alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality.
+---
+title: Compare with other libraries
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Compare with axios
+
+axios provides a very simple and easy-to-use HTTP request function based on promise. It only requires a simple line of code to send and receive requests, and can run in the browser and nodejs environment. It is a very excellent request js library.
+
+But axios focuses on sending requests and receiving responses, which means that if you need to write more code by yourself to actively optimize the request function, and alova is like a weapon of axios, combining axios with alova can get more powerful request capabilities. The following are the request management capabilities added by alova to axios.
+
+### alova provides automated request status management for axios
+
+When you only use axios, you usually need to maintain request-related status by yourself. You can get automated request status management capabilities by using alova's use hook.
+
+
+
+
+```javascript
+// vue3 example
+const loading = ref(false);
+const data = ref({});
+const error = ref(null);
+const request = async () => {
+ try {
+ loading.value = true;
+ data.value = await axios.get('/xxx');
+ } catch (e) {
+ error.value = e;
+ }
+ loading.value = false;
+};
+mounted(request);
+```
+
+
+
+
+```javascript
+// Use axios as alova's request adapter
+const { loading, data, error } = useRequest(alova.Get('/xxx'));
+```
+
+
+
+
+### alova provides high-performance request strategies out of the box
+
+alova provides you with [multiple high-performance request strategy modules](/tutorial/strategy). You can use different modules according to different request scenarios, which axios does not have.
+
+### alova provides response data cache for axios
+
+alova provides 3 caching modes to meet different caching scenarios, namely memory mode, cache occupying mode, and recovery mode. They are component-independent and can hit the cache as long as the request address and parameters are the same, unless you turn it off. Response data caching can greatly improve request fluency and reduce server pressure.
+
+### alova provides request sharing function for axios
+
+Request sharing will reuse the same request when sending multiple identical requests at the same time. It can also improve application fluency and reduce server pressure.
+
+### alova provides data pre-fetching for axios
+
+Requesting the data to be used in advance can also greatly improve application fluency.
+
+### alova can manage request states
+
+You can use alova across any component hierarchy to access stateful data in other components, which allows you to reduce some of the trouble of cross-component communication.
+
+## Compared with react-query and swr
+
+react-query is a powerful asynchronous state management, and swr is a React Hooks library for data requests. Their common feature is the use of use hooks to send and manage requests, and data caching functions. For them, alova has the following differences at.
+
+### alova has different goals
+
+In fact, alova's use hook also refers to the design of react-query and swr, but alova chooses the direction of the request strategy library. You can use different request strategy modules in different request scenarios, allowing you to write less code. At the same time, more efficient Client-Server data interaction can also be achieved.
+
+### Method proxy design
+
+Both react-query and swr use `axios` or `fetch api` directly in use hook to send requests, while alova uses the `Method` proxy design mode. This design has the following 3 benefits:
+
+1. Unified usage without different usage depending on the platform or UI framework.
+2. Request libraries such as `axios` and `fetch api` are decoupled from each API in the form of request adapters, which allows alova to provide a unified development experience and perfect coding migration.
+3. Each `Method` instance represents an API, you can aggregate the request parameters and request behavior parameters of the same API into the same `Method` instance without spreading them to different files, which is more suitable for managing a large number of APIs.
+4. alova realizes automatic management of response data cache by serializing request parameters on the `Method` instance. You do not need to specify the cache key, and both react-query and swr need to customize the `queryKey` to manage the cache.
+
+### High flexibility
+
+Alova achieves high flexibility through various adapters and middleware. It can not only run in any js environment, but also support users to customize request modules in different scenarios.
+
+### Lightweight
+
+alova is very lightweight, and its size is only 30%+ of react-query and axios. Similar in size to swr, but provides richer functionality.
diff --git a/versioned_docs/version-2.x/about/03-qa.md b/versioned_docs/version-2.x/about/03-qa.md
new file mode 100644
index 000000000..f36a19d80
--- /dev/null
+++ b/versioned_docs/version-2.x/about/03-qa.md
@@ -0,0 +1,133 @@
+---
+title: Question & Answer
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Why to create alova?
+
+Data requests have always been an indispensable and important part of applications. Since the birth of XMLHttpRequest, request schemes have emerged in endlessly. Client-side data interaction exploration has always focused on the simplicity of requests, such as `$.ajax`, `axios`, `fetch api` and Request tools such as `react-query`, the coding form continues to evolve from callback functions, Promise, to usehook. These js libraries have done a good job in making requests simple, but they only provide common functions, which means For different request scenarios such as sharing requests, paging requests, form submissions, uploading and downloading files, etc., developers still need to write complex codes themselves, which reduces development efficiency and performance cannot be guaranteed. As user experience becomes more and more important, In this era, application fluency has become more and more important.
+
+Additionally, the collaboration between client and server is also separated. Front-end engineers need to consult API documents and manually write API functions, and any changes of APIs need to actively notify front-end engineers, which will make your product more uncontrollable.
+
+**We think there is a simpler solution is that based on your request scenarios such as pagination, form submission, breakpoint resumption, etc., select the corresponding useHook, which will help you manage data and control when requests should be sent**. This allows developers to achieve more efficient Client-Server data interaction while writing little code.
+
+Additionally, alova has very flexible expansion capabilities to implement request strategies in different scenarios. You can also customize your own request scenarios. This part is in [Custom Chapter](/category/custom).
+
+In order to cover more request scenarios, we also abstracted the request scenarios into [Request scene model(RSM)](/tutorial/others/RSM), which explains alova's request strategy scheme well. In the future, alova will continue to carry forward our exploration of request strategies.
+
+## Alternative to the request libraries?
+
+alova is a request strategy library, which was originally created to provide specific request strategy solutions for different request scenarios, so as to achieve a smooth request experience more concisely and elegantly, such as `$.ajax`, `axios` and `fetch- api`, etc. provide good support for request sending and response receiving, they are an essential part of the [RSM](/tutorial/others/RSM) process (request events), alova still needs to dependent on them to make requests, so we can Think of alova as an weaponry of the request library, making the request library more powerful.
+
+## Why binding UI framework?
+
+Decoupling a js library means using it in more scenarios. For example, axios can be used in nodejs, but it also means that developers need to write more template code, such as using useHooks to encapsulate axios. However, alova abandons more usage scenarios brought about by decoupling, and positions the Scope of usage in conjunction with the UI framework to use alova in the most streamlined way. This is for the benefit of developers and is prevalent in a UI framework. Sometimes, deep binding can provide developers with direct-use functions and improve the developer's experience without requiring too much template code.
+
+## How to use alova via cdn?
+
+
+
+
+```html
+
+
+
+
+
+
+
+
+
+
+
Loading...
+
{{ error.message }}
+ responseData: {{ data }}
+
+
+
+
+```
+
+
+
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+:::tip
+
+svelte depends on the compilation tool and cannot be used directly through CDN. For details, see [svelte.dev](https://svelte.dev/)
+
+:::
+
+
+
+
+## What to pay attention to in React-Native?
+
+When using alova to develop React-Native applications, you can also use `alova/fetch`.
+
+But there are the following precautions:
+
+**Metro version**
+
+In alova's `package.json`, `exports` is used to define multiple export items, so you need to ensure these two points:
+
+1. Metro version is higher than 0.76.0
+
+2. Enable `resolver.unstable_enablePackageExports` in `metro.config.js`. [Click here for details](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental)
diff --git a/versioned_docs/version-2.x/about/_category_.json b/versioned_docs/version-2.x/about/_category_.json
new file mode 100644
index 000000000..c562b25fb
--- /dev/null
+++ b/versioned_docs/version-2.x/about/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Others",
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/versioned_docs/version-2.x/api/01-alova.md b/versioned_docs/version-2.x/api/01-alova.md
new file mode 100644
index 000000000..a7c058da6
--- /dev/null
+++ b/versioned_docs/version-2.x/api/01-alova.md
@@ -0,0 +1,329 @@
+---
+title: alova instance
+---
+
+## createAlova()
+
+Create an alova instance.
+
+- **type**
+
+```ts
+function createAlova(options?: AlovaOptions): Alova;
+```
+
+- **Parameters**
+
+1. config: configuration parameters
+
+| Parameter name | Type | Description |
+| -------------- | --------------------------- | --------------------------------------------------------------------------------------------------------- |
+| baseURL | string | Base path, empty by default, [View details](/tutorial/getting-started/alova) |
+| statesHook | object | State management hook, optional, [View details](/tutorial/combine-framework) |
+| requestAdapter | object | Request adapter, required, [View details](/tutorial/custom/custom-http-adapter) |
+| timeout | number | Timeout time, no timeout by default, [View details](/tutorial/getting-started/alova) |
+| localCache | object | Local cache configuration, default GET has 5000ms cache, [View details](/tutorial/cache/mode) |
+| storageAdapter | object | Local storage adapter, default is `localStorage`, [View details](/tutorial/custom/custom-storage-adapter) |
+| beforeRequest | function | Before request hook, [View details](/tutorial/getting-started/global-interceptor) |
+| responded | object \| function | Request response hook, [View details](/tutorial/getting-started/global-interceptor) |
+| shareRequest | boolean | Share request, [View details](/tutorial/getting-started/alova) |
+| errorLogger | boolean\| null \| function | Error log, [View details](/tutorial/advanced/error-logger) |
+| cacheLogger | boolean \| null \| function | Cache log, [View details](/tutorial/advanced/cache-logger) |
+
+- **return**
+
+Alova instance
+
+- **Example**
+
+```ts
+import { createAlova } from 'alova';
+
+const alova = createAlova({
+ baseURL: 'https://example.com',
+ statesHook: VueHook,
+ requestAdapter: GlobalFetch(),
+ timeout: 3000
+ // ...
+});
+```
+
+## alova.id
+
+The alova instance id is used to distinguish different alova instances. It can accurately match the method instance of the specified alova in [method instance matcher](/tutorial/advanced/method-matcher).
+
+- **Type**: string
+
+## alova.options
+
+When creating an alova instance through `createAlova`, the default configuration is merged with the passed-in configuration object.
+
+- **type**
+
+```ts
+interface AlovaOptions {
+ statesHook: StatesHook;
+ requestAdapter: AlovaRequestAdapter;
+ baseURL?: string;
+ timeout?: number;
+ localCache?: GlobalLocalCacheConfig;
+ storageAdapter?: AlovaStorageAdapter;
+ beforeRequest?: Function;
+ responded?: Function | ResponsedHandlerRecord;
+ shareRequest?: boolean;
+ errorLogger?: boolean | null | Function;
+ cacheLogger?: boolean | null | Function;
+}
+```
+
+## alova.storage
+
+The storage adapter instance corresponding to the alova instance defaults to `AlovaGlobalStorage`, which uses `localStorage`.
+
+- **type**
+
+```ts
+interface AlovaStorageAdapter {
+ get(key: string): any;
+ set(key: string, value: any): void;
+ remove(key: string): void;
+}
+```
+
+## alova.Get()
+
+Create a method instance for the GET request.
+
+- **type**
+
+```ts
+interface Alova {
+ Get(url: string, config?: AlovaMethodCreateConfig): Method;
+}
+```
+
+- **Parameters**
+
+1. url: request address
+2. config: configuration parameters
+
+| Parameter name | Type | Description |
+| -------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| headers | object | Request headers, [View details](/tutorial/getting-started/method) |
+| params | object | Request parameters, [View details](/tutorial/getting-started/method) |
+| name | string | method object name, in [updateState](/tutorial/advanced/update-across-components), [invalidateCache](/tutorial/cache/manually-invalidate), [setCache](/tutorial/cache/set-and-query), and [fetch function](/tutorial/advanced/use-fetcher), you can obtain the corresponding method instance by name or wildcard |
+| timeout | number | Request timeout, [View details](/tutorial/getting-started/method) |
+| localCache | LocalCacheConfig | Response cache time, [View details](/tutorial/cache/mode) |
+| hitSource | string | Hit the source method instance. When the source method instance request is successful, the cache of the current method instance will be invalidated. [View details](/tutorial/cache/auto-invalidate) |
+| enableDownload | boolean | Enable download progress information, [View details](/tutorial/combine-framework/download-upload-progress) |
+| enableUpload | boolean | Enable upload progress information, [View details](/tutorial/combine-framework/download-upload-progress) |
+| transformData | function | Transform response data, [View details](/tutorial/combine-framework/response) |
+| shareRequest | boolean | Request-level sharing request switch, [View details](/tutorial/getting-started/method) |
+
+> In addition to the configurable parameters above, other parameters supported by the request adapter are also supported.
+
+- **return**
+
+method instance
+
+- **Example**
+
+```ts
+const getUsers = alovaInstance.Get('/users', {
+ params: {
+ ID: 1
+ }
+ // ...
+});
+```
+
+## alova.Post()
+
+Create a method instance for the POST request.
+
+- **type**
+
+```ts
+interface Alova {
+ Post(
+ url: string,
+ data?: object | FormData | string | null,
+ config?: AlovaMethodCreateConfig
+ ): Method;
+}
+```
+
+- **Parameters**
+
+1. url: request address
+2. data: request body
+3. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget)
+
+- **return**
+
+method instance
+
+- **Example**
+
+```ts
+const postUsers = alovaInstance.Post(
+ '/createUser',
+ {
+ name: 'alova',
+ age: 18,
+ gender: 'male'
+ },
+ {
+ // Configuration parameters...
+ }
+);
+```
+
+## alova.Delete()
+
+Create a method instance for the DELETE request.
+
+- **type**
+
+```ts
+interface Alova {
+ Delete(
+ url: string,
+ data?: object | FormData | string | null,
+ config?: AlovaMethodCreateConfig
+ ): Method;
+}
+```
+
+- **Parameters**
+
+1. url: request address
+2. data: request body
+3. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget)
+
+- **return**
+
+method instance
+
+- **Example**
+
+```ts
+const deleteUsers = alovaInstance.Delete(
+ '/deleteUser',
+ {
+ ID: 1
+ },
+ {
+ // Configuration parameters...
+ }
+);
+```
+
+## alova.Put()
+
+Create a method instance for the PUT request.
+
+- **type**
+
+```ts
+interface Alova {
+ Put(
+ url: string,
+ data?: object | FormData | string | null,
+ config?: AlovaMethodCreateConfig
+ ): Method;
+}
+```
+
+- **Parameters**
+
+1. url: request address
+2. data: request body
+3. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget)
+
+- **return**
+
+method instance
+
+- **Example**
+
+```ts
+const putUsers = alovaInstance.Put(
+ '/updateUser',
+ {
+ id: 1,
+ name: 'alova'
+ },
+ {
+ // Configuration parameters...
+ }
+);
+```
+
+## alova.Head()
+
+Create a method instance of the HEAD request.
+
+- **type**
+
+```ts
+interface Alova {
+ Head(url: string, config?: AlovaMethodCreateConfig): Method;
+}
+```
+
+- **Parameters**
+
+1. url: request address
+2. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget)
+
+- **return**
+
+method instance
+
+## alova.Patch()
+
+Create a method instance for the PATCH request.
+
+- **type**
+
+```ts
+interface Alova {
+ Patch(
+ url: string,
+ data?: object | FormData | string | null,
+ config?: AlovaMethodCreateConfig
+ ): Method;
+}
+```
+
+- **Parameters**
+
+1. url: request address
+2. data: request body
+3. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget)
+
+- **return**
+
+method instance
+
+## alova.Options()
+
+Create a method instance of the OPTIONS request.
+
+- **type**
+
+```ts
+interface Alova {
+ Options(url: string, config?: AlovaMethodCreateConfig): Method;
+}
+```
+
+- **Parameters**
+
+1. url: request address
+2. config: configuration parameters, the parameter type is the same as [alova.Get](#alovaget)
+
+- **return**
+
+method instance
diff --git a/versioned_docs/version-2.x/api/02-method.md b/versioned_docs/version-2.x/api/02-method.md
new file mode 100644
index 000000000..3a97f7859
--- /dev/null
+++ b/versioned_docs/version-2.x/api/02-method.md
@@ -0,0 +1,486 @@
+---
+title: method instance
+---
+
+A method instance corresponds to a request information description, which has the URL, request headers, request parameters of a request, as well as request behavior parameters such as response data processing and cache data processing. Through method instances, you can feel a unified usage experience in any js environment, and it can run normally with very few changes. In addition, method instances put request parameters and request behavior parameters together, making it easier for APIs management instead of spreading it across multiple code files.
+
+## PromiseLike attribute
+
+After `[v2.16.0]`, the method instance is a PromiseLike instance, which has `then/catch/finally` functions, so you can use it as follows:
+
+```ts
+// Call the then function of method
+method.then(res => {
+ console.log(res);
+});
+
+// catch exception
+method.catch(e => {
+ console.log(e);
+});
+
+//Request completion call
+method.finally(() => {
+ console.log('Request completed');
+});
+```
+
+In addition, requests can also be sent through the `await method`.
+
+## new Method()
+
+Create a custom method instance.
+
+- **type**
+
+```ts
+interface MethodConstructor {
+ new (
+ type: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATCH',
+ context: Alova,
+ url: string,
+ config?: AlovaMethodCreateConfig,
+ data?: Arg | string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream
+ ): Method;
+ readonly prototype: Method;
+}
+```
+
+- **Parameters**
+
+1. `type`: request type
+2. `context`: alova instance
+3. `url`: request url
+4. `config`: Configuration parameters, the type is the same as config parameter type of [alova.Get](/api/alova#alovaget)
+5. `data`: request body data
+
+- **Example**
+
+```ts
+import { Method } from 'alova';
+import { alovaInstance } from './api';
+
+const method = new Method('GET', alovaInstance, '/api/users', {
+ params: {
+ ID: 1
+ }
+});
+```
+
+## getMethodKey()
+
+Get the key value of method. This key value is used as alova internal cache key.
+
+- **type**
+
+```ts
+function getMethodKey(method: Method): string;
+```
+
+- **Parameters**
+
+1. `method`: method instance
+
+- **return**
+
+The key value of the method instance passed in.
+
+- **Example**
+
+```ts
+import { getMethodKey } from 'alova';
+
+const method = alova.Get('/api/users');
+const methodKey = getMethodKey(method);
+```
+
+## matchSnapshotMethod()
+
+Obtain the requested method instance snapshot using the matching method of [method instance matcher](/tutorial/advanced/method-matcher) and return the matching result.
+
+- **type**
+
+```ts
+type MethodFilter =
+ | string
+ | RegExp
+ | {
+ name?: string | RegExp;
+ filter?: MethodFilterHandler;
+ alova?: Alova;
+ };
+function matchSnapshotMethod(
+ matcher: MethodFilter,
+ matchAll?: boolean
+): Method[] | Method | undefined;
+```
+
+- **Parameters**
+
+1. `matcher`: method instance matcher
+2. `matchAll`: Whether to match all, the default is true
+
+- **return**
+
+Returns an array of method instances when `matchAll` is true, otherwise returns a method instance or undefined
+
+- **Example**
+
+```ts
+import { matchSnapshotMethod } from 'alova';
+
+await alova.Get('/api/users');
+const snapshotMethod = matchSnapshotMethod({
+ name: 'user',
+ filter(method, i, methodArray) {
+ return method.url.includes('users');
+ },
+ alova: alova
+});
+```
+
+## method.headers
+
+Request header.
+
+- **type**
+
+```ts
+interface Method {
+ headers?: any;
+}
+```
+
+## method.baseURL
+
+The base path of the request, inherited from [alova instance](/api/alova).
+
+- **type**
+
+```ts
+interface Method {
+ baseURL: string;
+}
+```
+
+## method.url
+
+Create the url of the method instance.
+
+- **type**
+
+```ts
+interface Method {
+ url: string;
+}
+```
+
+## method.type
+
+Request type.
+
+- **type**:
+
+```ts
+interface Method {
+ type: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'PATCH';
+}
+```
+
+## method.data
+
+Request body.
+
+- **type**
+
+```ts
+interface Method {
+ data?: any;
+}
+```
+
+## method.context
+
+Create an alova instance of the current method.
+
+- **type**
+
+```ts
+interface Method {
+ context: Alova;
+}
+```
+
+## method.hitSource
+
+Hitting the source method instance, when the source method instance request succeeds, the cache of the current method instance will be invalidated. As an automatic invalidation function, you only need to set the hit source instead of manually calling `invalidateCache` to invalidate the cache. In addition, this function is more concise and effective than the `invalidateCache` method in complex invalidation relationships. The field value can be set to the name of the method instance, other method instances, name regular matching, or their array.
+
+- **type**
+
+```ts
+interface Method {
+ hitSource?: Method | string | RegExp | (Method | string | RegExp)[];
+}
+```
+
+## method.meta
+
+The metadata of method is used to record request feature information, [View details](/tutorial/getting-started/method-metadata).
+
+- **type**
+
+```ts
+interface Method {
+ meta?: any;
+}
+```
+
+## method.config
+
+Configuration information when creating a method through `alova.Get/alova.Post` and other methods, [View details](/api/alova#alovaget).
+
+- **type**
+
+```ts
+interface Method {
+ config: AlovaMethodCreateConfig;
+}
+```
+
+## method.fromCache
+
+Is the response of current request from cache.
+
+- **type**
+
+```ts
+interface Method {
+ fromCache: boolean;
+}
+```
+
+## method.send()
+
+Use this method instance to send the request directly. If you send the request after `[v2.16.0]`, you can omit calling this method.
+
+- **type**
+
+```ts
+interface Method {
+ send(forceRequest?: boolean): Promise;
+}
+```
+
+- **Parameters**
+
+1. `forceRequest`: whether to force the request, the default is false
+
+- **return**
+
+A Promise instance with response data.
+
+- **Example**
+
+```ts
+const method = alova.Get('/api/users');
+const response = await method.send();
+```
+
+## method.abort()
+
+Abort the current request.
+
+- **type**
+
+```ts
+interface Method {
+ abort(): void;
+}
+```
+
+- **Example**
+
+```ts
+const method = alova.Get('/api/users');
+method.abort();
+```
+
+## method.then()
+
+After `[v2.16.0]`, the method instance is a PromiseLike instance. You can directly call this method or `await method` to send a request and obtain the response data.
+
+- **type**
+
+```ts
+interface Method {
+ then(
+ onFulfilled?: (value: Response) => any,
+ onRejected?: (reason: any) => any
+ ): Promise;
+}
+```
+
+- **Parameters**
+
+1. `onFulfilled`: callback function when the request is successful
+2. `onRejected`: callback function when the request fails
+
+- **return**
+
+A Promise instance with response data.
+
+- **Example**
+
+```ts
+const method = alova.Get('/api/users');
+const response = await method;
+```
+
+## method.catch()
+
+After `[v2.16.0]`, the method instance is a PromiseLike instance. This method can be called directly to send requests and catch errors.
+
+- **type**
+
+```ts
+interface Method {
+ catch(
+ onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null
+ ): Promise;
+}
+```
+
+- **Parameters**
+
+1. `onrejected`: callback function when request error
+
+- **return**
+
+Promise instance.
+
+- **Example**
+
+```ts
+const method = alova.Get('/api/users');
+const response = await method.catch(error => {
+ console.error('Request error');
+});
+```
+
+## method.finally()
+
+After `[v2.16.0]`, the method instance is a PromiseLike instance. This method can be called directly to send the request and handle the response completion.
+
+- **type**
+
+```ts
+interface Method {
+ finally(onfinally?: (() => void) | undefined | null): Promise;
+}
+```
+
+- **return**
+
+Promise instance.
+
+- **Example**
+
+```ts
+const method = alova.Get('/api/users');
+const response = await method.finally(() => {
+ console.log('Request completed');
+});
+```
+
+## method.onDownload()
+
+Bind the download event to obtain download progress information.
+
+- **type**
+
+```ts
+interface Method {
+ onDownload(handler: ProgressHandler): () => void;
+}
+```
+
+- **Parameters**
+
+1. `handler` download event callback function
+
+- **return**
+
+unbind function
+
+- **Example**
+
+```ts
+const method = alova.Get('/api/download_file');
+const offEvent = method.onDownload(event => {
+ console.log('File size:', event.total);
+ console.log('Downloaded:', event.loaded);
+});
+
+offEvent();
+```
+
+## method.onUpload()
+
+Bind the upload event to obtain upload progress information.
+
+- **type**
+
+```ts
+interface Method {
+ onUpload(handler: ProgressHandler): () => void;
+}
+```
+
+- **Parameters**
+
+1. `handler` upload event callback function
+
+- **return**
+
+unbind function
+
+- **Example**
+
+```ts
+const method = alova.Get('/api/upload_file', formData);
+const offEvent = method.onUpload(event => {
+ console.log('File size:', event.total);
+ console.log('Uploaded:', event.loaded);
+});
+
+offEvent();
+```
+
+## method.setName()
+
+Set the name of the method instance.
+
+- **type**
+
+```ts
+interface Method {
+ setName(name: string | number): void;
+}
+```
+
+- **Parameters**
+
+1. `name`: the name of the method instance
+
+- **return**
+
+none
+
+- **Example**
+
+```ts
+const method = alova.Get('/api/users', {
+ name: 'user'
+});
+method.setName('newUser');
+```
diff --git a/versioned_docs/version-2.x/api/03-core-hooks.md b/versioned_docs/version-2.x/api/03-core-hooks.md
new file mode 100644
index 000000000..12b496e22
--- /dev/null
+++ b/versioned_docs/version-2.x/api/03-core-hooks.md
@@ -0,0 +1,267 @@
+---
+title: Core useHooks
+---
+
+## useRequest
+
+Represents the sending of a request. When executing useRequest, a request will be sent by default, and stateful request-related data will be created and maintained, such as `loading/data/error`, etc. It is the most commonly used method when obtaining initial data on the page. It also supports turning off its default request sending, which is very useful in request scenarios triggered by click events such as submitting data.
+
+> Go to [Send Request](/tutorial/combine-framework/use-request) for details.
+
+### type
+
+```ts
+function useRequest(
+ methodHandler: Method | (...args: any[]) => Method,
+ config?: RequestHookConfig
+): UseHookReturnType;
+```
+
+### Parameters
+
+1. `methodHandler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned.
+2. `config`: hook configuration parameters.
+
+| Name | Description | Type | Default | Version |
+| ------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- |
+| immediate | Whether to initiate the request immediately | boolean | true | - |
+| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - |
+| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false | - | - |
+| managedStates | Additional managed states, which can be updated via updateState | Record\ | - | - |
+| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
+
+#### AlovaFrontMiddlewareContext
+
+| Name | Description | Type | Version |
+| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
+| method | The method object of the current request | Method | - |
+| cachedResponse | Hit cached data | any | - |
+| config | Current use hook configuration | Record\ | - |
+| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
+| frontStates | use hook front-end state collection, such as data, loading, error, etc. | [FrontRequestState](#frontrequeststate) | - |
+| send | Send request function | (...args: any[]) => Promise | - |
+| abort | abort function | () => void | - |
+| decorateSuccess | Decorate success callback function | (decorator: ( handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void, event: [AlovaSuccessEvent](#alovasuccessevent), index: number, length: number ) => void) => void | - |
+| decorateError | Decoration failure callback function | (decorator: ( handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void, event: [AlovaErrorEvent](#alovaerrorevent), index: number, length: number ) => void) => void | - |
+| decorateComplete | Decoration completion callback function | (decorator: ( handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent), index: number, length: number ) => void) => void | - |
+| update | Function to update the current use hook front-end state, more useful in react | (newFrontStates: [FrontRequestState](#frontrequeststate)) => void; | - |
+| controlLoading | Customize the loading state of the control. The call will no longer trigger changes in the loading state. When the incoming control is false, the control will be cancelled. | (control?: boolean) => void | - |
+
+#### AlovaGuardNext
+
+```typescript
+type AlovaGuardNext = (guardNextConfig?: {
+ force?: boolean | (...args: any[]) => boolean;
+ method?: Method;
+}): Promise;
+```
+
+#### FrontRequestState
+
+The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| loading | request loading status | boolean | - |
+| data | response data | any | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+#### AlovaSuccessEvent
+
+| Name | Description | Type | Version |
+| --------- | ---------------------------------------------------------------------------------------- | ------- | ------- |
+| method | The method object of the current request | Method | - |
+| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
+| data | response data | any | - |
+| fromCache | Whether the response data comes from cache | boolean | - |
+
+#### AlovaErrorEvent
+
+| Name | Description | Type | Version |
+| -------- | ---------------------------------------------------------------------------------------- | ------ | ------- |
+| method | The method object of the current request | Method | - |
+| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
+| error | Response error instance | Error | - |
+
+#### AlovaCompleteEvent
+
+| Name | Description | Type | Version |
+| --------- | ---------------------------------------------------------------------------------------- | -------------------- | ------- |
+| method | The method object of the current request | Method | - |
+| sendArgs | Parameters of the response processing callback, which are passed in by send of use hooks | any[] | - |
+| status | Response status, success when successful, error when failure | 'success' \| 'error' | - |
+| data | response data, with value when successful | any | - |
+| fromCache | Whether the response data comes from the cache, a value if successful | boolean | - |
+| error | Response error instance, with value in case of failure | Error | - |
+
+### return value
+
+`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3 and the `Ref` type in react. It is a normal value and is of `Writable` type in svelte.
+
+#### Responsive data
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| loading | request loading status | boolean | - |
+| data | response data | any | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+#### Operation function
+
+| name | description | function parameters | return value | version |
+| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- |
+| send | Send request function | ...args: any[] | - | - |
+| abort | abort function | - | Promise | - |
+| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
+
+#### event
+
+| name | description | callback parameters | version |
+| ---------- | -------------------------------- | ------------------------------------------------ | ------- |
+| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - |
+| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - |
+| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - |
+
+## useWatcher
+
+Monitor the status and initiate a request after the status changes. In some scenarios that require re-requesting as the data changes, such as paging, data filtering, and fuzzy search.
+
+> Go to [State Change Request](/tutorial/combine-framework/use-watcher) for details.
+
+### type
+
+```typescript
+function useWatcher(
+ handler: Method | (...args: any[]) => Method,
+ watchingStates: State[],
+ config?:WatcherHookConfig
+): UseHookReturnType;
+```
+
+### Parameters
+
+1. `handler`: can be passed in two forms: method instance and function. When specified as a function, it can receive parameters passed in by `send` and require a method instance to be returned.
+2. `config`: hook configuration parameters.
+
+| Name | Description | Type | Default | Version |
+| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- |
+| immediate | Whether to initiate the request immediately | boolean | true | - |
+| initialData | The initial data value. The data value is the initial value before the first response. If it is not set, it is `undefined` | any | - | - |
+| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean \| (...args: any[]) => boolean \| false |
+| managedStates | Additional managed states, which can be updated via updateState | Record\ | - |
+| debounce | Request debounce time (milliseconds), when passing in the array, you can set the debounce time individually in the order of watchingStates | number \| number[] | - |
+| middleware | Middleware function, [Understanding alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFrontMiddlewareContext](#alovafrontmiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
+| sendable | Whether to send a request when the monitored state changes | (methodInstance: AlovaEvent) => boolean | () => true | - |
+| abortLast | Whether to abort the last unresponsive request | boolean | true | - |
+
+### return value
+
+`UseHookReturnType` contains response data and request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3 and ordinary value in react. In svelte it is Writable type.
+
+#### Responsive data
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| loading | request loading status | boolean | - |
+| data | response data | any | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+#### Operation function
+
+| name | description | function parameters | return value | version |
+| ------ | ----------------------------------------------------------------------------- | ------------------------------------------------------- | ------------ | ------- |
+| send | Send request function | ...args: any[] | Promise | - |
+| abort | abort function | - | - | - |
+| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
+
+#### event
+
+| name | description | callback parameters | version |
+| ---------- | -------------------------------- | ------------------------------------------------ | ------- |
+| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovasuccessevent) | - |
+| onError | Request error event binding | event: [AlovaErrorEvent](#alovaerrorevent) | - |
+| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent) | - |
+
+## useFetcher
+
+It is used to pull data through `useFetcher`, which is useful when preloading data and updating status across modules.
+
+> Go to [Data Fetching](/tutorial/advanced/use-fetcher) to view details.
+
+### type
+
+```typescript
+function useFetcher(config?: FetcherHookConfig): UseFetchHookReturnType;
+```
+
+### Parameters
+
+1. `config`: hook configuration parameters.
+
+| Name | Description | Type | Default | Version |
+| ---------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------- |
+| force | Whether to force the request, it can be set to the function to dynamically return a boolean value | boolean | (...args: any[]) => boolean \| false | - |
+| middleware | Middleware function, [Understand alova middleware](/tutorial/advanced/middleware) | (context: [AlovaFetcherMiddlewareContext](#alovafetchermiddlewarecontext), next: [AlovaGuardNext](#alovaguardnext)) => Promise\ | - | - |
+
+#### AlovaFetcherMiddlewareContext
+
+| Name | Description | Type | Version |
+| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
+| method | The method object of the current request | Method | - |
+| cachedResponse | Hit cached data | any | - |
+| config | Current use hook configuration | Record\ | - |
+| fetchArgs | Parameters of the response processing callback, which are passed in by fetch of useFetcher | any[] | - |
+| fetchStates | use hook preload state collection, such as fetching, error, etc. | [FetchRequestState](#fetchrequeststate) | - |
+| fetch | Data preloading function | (method: Method, ...args: any[]) => Promise | - |
+| abort | abort function | () => void | - |
+| decorateSuccess | Decorate success callback function | (decorator: ( handler: (event: [AlovaSuccessEvent](#alovasuccessevent)) => void, event: [AlovaSuccessEvent](#alovasuccessevent), index: number, length: number ) => void) => void | - |
+| decorateError | Decoration failure callback function | (decorator: ( handler: (event: [AlovaErrorEvent](#alovaerrorevent)) => void, event: [AlovaErrorEvent](#alovaerrorevent), index: number, length: number ) => void) => void | - |
+| decorateComplete | Decoration completion callback function | (decorator: ( handler: (event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent), index: number, length: number ) => void) => void | - |
+| update | Function to update the current use hook preloading state, more useful in react | (newFrontStates: [FetchRequestState](#fetchrequeststate)) => void; | - |
+| controlFetching | After calling, the state of fetching will be customized and the change of fetching state will no longer be triggered internally. When the incoming control is false, the control will be canceled | (control?: boolean) => void | - |
+
+#### FetchRequestState
+
+The following attribute values will automatically infer the responsive data type of the corresponding UI framework based on `statesHook`, which is the `Ref` type in vue3, the normal value in react, and the `Writable` type in svelte
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| fetching | preloading request status | boolean | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+### return value
+
+`UseFetchHookReturnType` contains request-related states, operation functions and event binding functions. They will automatically infer the responsive data type of the corresponding UI framework based on statesHook. It is Ref type in vue3, ordinary value in react, and ordinary value in svelte. For Writable type.
+
+#### Responsive data
+
+| Name | Description | Type | Version |
+| ----------- | ----------------------------- | ------------------ | ------- |
+| fetching | preloading request status | boolean | - |
+| error | Request error information | Error \| undefined | - |
+| downloading | Download progress information | Object | - |
+| uploading | Upload progress information | Object | - |
+
+#### Operation function
+
+| name | description | function parameters | return value | version |
+| ------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------ | ------- |
+| fetch | Data preloading function | 1. method: preloaded Method instance 2. ...args: any[] | Promise | - |
+| abort | abort function | - | - | - |
+| update | Function to update the current use hook front-end state, more useful in react | newFrontStates: [FrontRequestState](#frontrequeststate) | - |
+
+#### event
+
+| name | description | callback parameters | version |
+| ---------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- |
+| onSuccess | Request success event binding | event: [AlovaSuccessEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovasuccessevent) | - |
+| onError | Request error event binding | event: [AlovaErrorEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovaerrorevent) | - |
+| onComplete | Request completion event binding | event: [AlovaCompleteEvent](#alovacompleteevent)) => void, event: [AlovaCompleteEvent](#alovacompleteevent) | - |
diff --git a/versioned_docs/version-2.x/api/04-cache.md b/versioned_docs/version-2.x/api/04-cache.md
new file mode 100644
index 000000000..0a5339a22
--- /dev/null
+++ b/versioned_docs/version-2.x/api/04-cache.md
@@ -0,0 +1,132 @@
+---
+title: Cache operation
+---
+
+## invalidateCache()
+
+Active cache invalidation.
+
+> Go to [Manually invalidate cache](/tutorial/cache/manually-invalidate) for details.
+
+- **type**
+
+```ts
+type MethodFilter =
+ | string
+ | RegExp
+ | {
+ name?: string | RegExp;
+ filter?: MethodFilterHandler;
+ alova?: Alova;
+ };
+function invalidateCache(matcher?: Method | Method[] | MethodFilter): void;
+```
+
+- **Parameters**
+
+1. `matcher`: Cache invalid matcher, the value is a method instance or array, or it can be set to [method instance matcher](/tutorial/advanced/method-matcher).
+
+- **return**
+
+none
+
+- **Example**
+
+```ts
+import { invalidateCache } from 'alova';
+
+invalidateCache(method);
+invalidateCache([method1, method2]);
+invalidateCache({
+ name: 'userMethod',
+ filter: method => method.name === 'method1'
+});
+```
+
+## setCache()
+
+Set up response caching.
+
+> Go to [Cache Update and Query](/tutorial/cache/set-and-query) for details.
+
+- **type**
+
+```ts
+type MethodFilter =
+ | string
+ | RegExp
+ | {
+ name?: string | RegExp;
+ filter?: MethodFilterHandler;
+ alova?: Alova;
+ };
+function setCache(
+ matcher: Method | Method[] | MethodFilter,
+ dataOrUpdater: R | ((oldCache: R) => R | undefined | void)
+): void;
+```
+
+- **Parameters**
+
+1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/tutorial/advanced/method-matcher), which will match all matching The method instance of the condition sets the cached data.
+2. `dataOrUpdater`: Cache data or update function. If it is a function, it needs to return new cached data. If it returns `undefined` or does not return, the update will be cancelled.
+
+- **return**
+
+none
+
+- **Example**
+
+```ts
+import { setCache } from 'alova';
+
+setCache(method, {});
+setCache([method1, method2], {});
+setCache(
+ {
+ name: 'userMethod',
+ filter: method => method.name === 'method1'
+ },
+ {}
+);
+```
+
+## queryCache()
+
+Query cache.
+
+> Go to [Cache Update and Query](/tutorial/cache/set-and-query) for details.
+
+- **type**
+
+```ts
+type MethodFilter =
+ | string
+ | RegExp
+ | {
+ name?: string | RegExp;
+ filter?: MethodFilterHandler;
+ alova?: Alova;
+ };
+function queryCache(matcher?: Method | MethodFilter): R | undefined;
+```
+
+- **Parameters**
+
+1. `matcher`: The value is method instance, method name string, method name regular expression. It can also be set to [method instance matcher](/tutorial/advanced/method-matcher), which will meet the conditions. The first method instance queries cached data.
+
+- **return**
+
+Cache data, or return `undefined` if not cached.
+
+- **Example**
+
+```ts
+import { queryCache } from 'alova';
+
+const responseCache = queryCache(method);
+const responseCache = queryCache({
+ name: 'userMethod',
+ filter: method => method.name === 'method1'
+});
+```
diff --git a/versioned_docs/version-2.x/api/05-states.md b/versioned_docs/version-2.x/api/05-states.md
new file mode 100644
index 000000000..5c161e14b
--- /dev/null
+++ b/versioned_docs/version-2.x/api/05-states.md
@@ -0,0 +1,119 @@
+---
+title: Response states operation
+---
+
+## updateState
+
+Manually update existing response data or additional status under any module/page.
+
+**⚠️ Please make sure the component is not destroyed**
+
+By default, `updateState` will look for the response state created by alova's useHooks when sending a request, but to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response status has not been destroyed, otherwise the corresponding response status will not be found and the update will fail.
+
+This problem often occurs when updating status across pages, because what we tend to overlook when the page jumps is that the previous page has been destroyed by default. Therefore, if you want to update status across pages, here are two suggestions :
+
+1. Persist the page components to ensure that the updated status can still be found;
+2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect.
+
+> Go to [Cross-page/module update response states](/tutorial/advanced/update-across-components) for details.
+
+> To use updateState to manage extra states, please refer to [Extra State Management](/tutorial/advanced/manage-extra-states).
+
+- **type**
+
+```ts
+type MethodFilter =
+ | string
+ | RegExp
+ | {
+ name?: string | RegExp;
+ filter?: MethodFilterHandler;
+ alova?: Alova;
+ };
+function updateState(
+ matcher: Method | MethodFilter,
+ handleUpdate: UpdateStateCollection['data'] | UpdateStateCollection,
+ options?: UpdateOptions
+): boolean;
+```
+
+- **Parameters**
+
+- `matcher`: The value is method instance, method name string, method name regular expression, it can also be set to [method instance matcher](/tutorial/advanced/method-matcher), if it matches the qualified method, `handleUpdate` will be called.
+- `handleUpdate`: update function or update function collection. If it is a function collection, the corresponding update function on the collection will be called and the return value will be used as the update result.
+- `options`: optional options.
+
+| Parameter name | Type | Description |
+| -------------- | -------- | -------------------------------------------------- |
+| onMatch | Function | After matching method, the function will be called |
+
+- **return**
+
+Whether the update is successful.
+
+- **Example**
+
+Use `useRequest` in page or component A to send a request and get the response data.
+
+```ts
+const { data } = useRequest(
+ alova.Get('/api/user', {
+ name: 'user'
+ })
+);
+```
+
+Use `updateState` in page or component B to update the response state.
+
+```ts
+import { updateState } from 'alova';
+
+// Match by method instance
+updateState(alova.Get('/api/user'), oldData => {
+ return [
+ ...oldData,
+ {
+ id: 10000,
+ name: 'Alova',
+ },
+ ]
+});
+
+// Match by method name
+updateState('user', oldData => {
+ return [
+ ...oldData,
+ {
+ id: 10000,
+ name: 'Alova',
+ },
+ ];
+}
+
+// Regular matching through method name
+updateState(/^us/, oldData => {
+ return [
+ ...oldData,
+ {
+ id: 10000,
+ name: 'Alova',
+ },
+ ];
+})
+
+// Match through method instance matcher
+updateState({
+ name: 'user',
+ filter(method, i, methods) {
+ return methods.length === i + 1;
+ }
+}, oldData => {
+ return [
+ ...oldData,
+ {
+ id: 10000,
+ name: 'Alova',
+ },
+ ];
+});
+```
diff --git a/versioned_docs/version-2.x/api/06-global-config.md b/versioned_docs/version-2.x/api/06-global-config.md
new file mode 100644
index 000000000..a3feb3094
--- /dev/null
+++ b/versioned_docs/version-2.x/api/06-global-config.md
@@ -0,0 +1,35 @@
+---
+title: Global configuration
+---
+
+## globalConfig()
+
+Global configuration.
+
+- **type**
+
+```ts
+function globalConfig(config: AlovaGlobalConfig): void;
+```
+
+- **Parameters**
+
+1. config: configuration
+
+| Parameter name | Type | Description |
+| -------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
+| limitSnapshots | number | method snapshot number limit, set to 0 to disable saving snapshots. After closing, the method snapshot matcher will be unavailable |
+
+- **return**
+
+none
+
+- **Example**
+
+```ts
+import { globalConfig } from 'alova';
+
+globalConfig({
+ limitSnapshots: 10
+});
+```
diff --git a/versioned_docs/version-2.x/api/_category_.json b/versioned_docs/version-2.x/api/_category_.json
new file mode 100644
index 000000000..de7065684
--- /dev/null
+++ b/versioned_docs/version-2.x/api/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "API",
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/versioned_docs/version-2.x/contributing/01-overview.md b/versioned_docs/version-2.x/contributing/01-overview.md
new file mode 100644
index 000000000..a463091e8
--- /dev/null
+++ b/versioned_docs/version-2.x/contributing/01-overview.md
@@ -0,0 +1,174 @@
+---
+title: Contribution Guidelines
+---
+
+# alova Contribution Guidelines
+
+Hello, I'm glad to meet you here. This is a detailed alova contribution guidelines, which includes detailed guidance on contributing to all aspects of alova. Please continue reading.
+
+## Preface
+
+In the past period of time, we have received active participation information from developers from all over the world in Github issues and Github Discussion, which means that alova is being loved by more and more developers. Even so, alova is still a rookie, and it still has a long way to go.
+
+**We expect to make alova a common project for everyone who is willing to participate, and we encourage everyone to become a contributor to the alova community with an open and inclusive attitude. Moreover, we believe that contributing to alova is not limited to code contributions, but participating in any activities that are beneficial to the development of alova is considered to contribute to alova.** Participating in contributions now can win you more effective contribution opportunities, and it allows you to contribute to the world Even if you are a junior developer, as long as the idea is in line with [alova's mission and design principles](#alova-mission-and-design-principles), please generously participate!
+
+> There is a [Code of conduct](./code-of-conduct) here, please refer to it.
+
+## Contribution directory
+
+Here are 13 places where you can contribute, but not limited to these, you can choose the part you want to participate in, and link to the corresponding location for detailed reading:
+
+- [use alova in your project](#use-alova-in-your-project)
+- [star alova](#star-alova)
+- [report bug](#report-bug)
+- [Propose new feature ideas](#propose-new-feature-ideas)
+- [Pull Request](#pull-request)
+- [Create an adapter or strategy library based on alova](#create-an-adapter-or-strategy-library-based-on-alova)
+- [Participate in community/PR review](#participate-in-community-pr-review)
+- [Publish and disseminate information about alova](#publish-and-disseminate-information-about-alova)
+- [Share experience](#share-experience)
+- [Collaboration](#collaboration)
+- [Donation](#donation)
+- [Correct or add docs](#correct-or-add-docs)
+- [Translate docs](#Translate-docs)
+
+## alova mission and design principles
+
+### alova Mission
+
+alova's mission gives it a clear development direction, which clearly defines what alova should do.
+
+alova is a lightweight request strategy library, **Its mission is to enable developers to achieve more efficient Client-Server data interaction by less codes**.
+
+For developers, alova provides them with a simple API and out-of-the-box advanced request functions, as well as various simple and high-performance request strategy modules. For application users, they can enjoy the benefits of alova The smooth experience brought by high-performance data interaction, therefore, alova has the following features:
+
+1. The api design is similar to axios, allowing users to learn at a lower cost;
+2. Deep binding of the UI framework, greatly improving the benefits of developers;
+3. Out-of-the-box advanced functions to avoid repeated packaging, such as request sharing, request caching, etc., to reduce developers' repeated packaging;
+4. The platform-independent coding way, and it can perfectly migrate in different platform;
+5. High scalability design, which can encapsulate high-reusability and high-performance business-related request strategies;
+6. Highly aggregated and low-coupling method design improves API code maintainability;
+
+### alova design principles
+
+The design principles points out how it should be designed, the following is the core design concept of alova.
+
+1. `Method` proxy design, high aggregation, platform-independent design, throughout the request, you should be able to access it in any request function, from another perspective, the information related to the request should also be placed in the method instance ;
+2. Lightweight, try to keep the source code concise in coding, such as avoiding repeated code, merging variable declarations, prototype chain function encapsulation, no similar API, tree shaking, but long variable names are allowed, because it will be used when compiling will be replaced by a single letter;
+3. Highly scalable design. First, the design of alova uses a large number of adapter patterns and hook functions. For example, adapters include `requestAdapter`, `storageAdapter`, etc., and hook functions include `beforeRequest`, `responded`, `transformData`, `localCache`, etc., and most of them have default behaviors, which are designed to be easy to use while retaining high scalability; second, global request parameters can be overwritten, such as `timeout`, `shareRequest`, etc., for These parameters can be set individually for special requests.
+4. The api design is universal. First, it means that the function of this api has a high level of abstraction, rather than a specific business. Second, the api design is scalable to adapt to the needs of the api iteration
+
+> The api universal design is only applicable to the alova library. If you are conceiving a request strategy, you can design it according to the specific business.
+
+## Select the contribution point you are interested in
+
+### Use alova in your project
+
+We believe that your use of alova in the project is also a contributor to alova, which is also telling people that alova is a trustworthy open source project, please open [this issue](https://github.com/alovajs/alova/issues/165), which may give you the opportunity to display your project on the alova official website.
+
+### Star alova
+
+Although this may be considered insignificant, it represents your recognition of alova, and every star is very important to alova. star alova in the right top of [alova's Github repository](https://github.com/alovajs/alova), which is important to us.
+
+### Report bug
+
+Please move to [Github new issue](https://github.com/alovajs/alova/issues/new/choose) and select the corresponding template to submit. Detailed instructions will be displayed in the submitted issue.
+
+**PLEASE NOTE:** If you want to ask questions about alova, please go to [Github Discussion](https://github.com/alovajs/alova/discussions) to create a question. That would be closed immediately when ask a question in issues.
+
+### Propose new feature ideas
+
+In order for alova to achieve its value and goals, before submitting a new feature idea, please carefully read [alova mission and design principles](#alova-mission-and-design-principles), and ensure that your new idea is in line with alova's mission and design philosophy design concept.
+
+Then, please submit it in [🚀 New Feature Proposal](https://github.com/alovajs/alova/issues/new?assignees=&labels=feature-request&projects=&template=FEATURE_REQUEST_en.yml), the detailed description will be Displayed when submitting an issue.
+
+### Pull Request
+
+You can contribute the following 3 aspects of code through pull request. If you are a new partner who is interested in participating, all `good first issue` issues are listed in [Github contribution list](https://github.com/alovajs/alova/contribute), it is used to Tell new partners who are interested in contributing that this is a good start.
+
+#### Bug fix
+
+Issues marked as [`bug:confirmed`](https://github.com/alovajs/alova/labels/bug%3Aconfirmed) in Github issues are all confirmed bugs, you can choose freely.
+
+If you encounter a bug yourself, please also [report a bug](#report-bug) first to ensure that the bug is confirmed to avoid invalid pull requests.
+
+#### New feature development
+
+Issues marked as [`feature-request:confirmed`](https://github.com/alovajs/alova/labels/feature-request%3Aconfirmed) in Github issues are new features that have been confirmed, you You can choose freely.
+
+If you have an idea for adding a new feature, please also [submit an issue of a new feature idea](#propose-new-feature-ideas) to ensure that the idea is confirmed to avoid invalid pull requests.
+
+#### Project configuration
+
+If you are very good at project configuration and find deficiencies in the alova project, such as incomplete configuration, too old configuration version, insufficient automation (including project development automation and Github warehouse management automation), you can also press [New Feature development](#new-feature-development) process to contribute.
+
+:::warning IMPORTANT
+
+1. Please read the [Developing Guidelines](./developing-guidelines) carefully before developing, it can guide you step by step on how to contribute code.
+2. When you identify an issue that needs to be resolved, please make sure that it has not been flagged by someone else's pull request, which means that it has been pre-occupied.
+
+:::
+
+### Create an adapter or strategy library based on alova
+
+alova provides high-extensibility features, and you can write your own js library based on it.
+
+#### Custom Adapter
+
+Customize various adapters to meet the operating requirements in different environments. The following directions are available for reference:
+
+1. Customize statesHook, which can be executed under different UI frameworks, such as `solid/qwik`, currently supports `react/vue/svelte`, please read [Custom statesHook](/tutorial/custom/custom-stateshook);
+2. Customize the request adapter, so that alova can cooperate with more request schemes, such as `GraphQL/SSE`, etc.;
+3. Customize the storage adapter to meet the storage requirements of different environments, such as `react-native`;
+4. Any combination of the above, such as the official [uniapp adapter](https://github.com/alovajs/adapter-uniapp), which includes request adapters and storage adapters.
+
+#### Custom request strategy
+
+Request strategies can help developers write high-performance functions more efficiently. Although the official [@alova/scene](/tutorial/strategy) provides some common request strategies, it is not enough It is a good choice to customize your own reusable request strategy based on alova to meet the business scenarios related to various requests of developers, and you can also publish them on npm for everyone to use.
+
+:::tip Submit your project
+
+If you have written an alova-based js library, please submit your project in [this issue](https://github.com/alovajs/alova/issues/165), which will allow your project to be displayed on the alova official website Opportunity.
+
+:::
+
+### Participate in community/PR review
+
+If you are interested in technical communication, then it may be more suitable for you to participate in more community communication. You can participate in the discussion of bugs and new features in Github issues, or in [Github Discussion](https://github.com/alovajs/alova/discussions), [Discord](https://discord.gg/S47QGJgkVb) or [QQ channel](https://pd.qq.com/s/1cdjx0nnw) to answer questions for others, which allows you to communicate with people from all over the world, which is a very interesting thing.
+
+At the same time, you can also participate in PR review in [pull request](https://github.com/alovajs/alova/pulls), which is also a topic of communication.
+
+### Publish and disseminate information about alova
+
+You can publish or repost any information that is beneficial to the development of alova on any social platform, short video platform, or technology sharing platform, which will help increase the influence of alova. We will filter out relevant articles or videos and display them on the alova official website. Here are some good articles:
+
+- [It’s time to replace your axios](https://medium.com/@huzhen555/its-time-to-replace-your-axios-12c014833b04)
+- [Alova.js 筆記-試用相較 axios 更輕量、更高集成的請求庫](https://uu9924079.medium.com/alova-js-%E7%AD%86%E8%A8%98-%E8%A9%A6%E7%94%A8%E7%9B%B8%E8%BC%83-axios-%E6%9B%B4%E8%BC%95%E9%87%8F-%E6%9B%B4%E9%AB%98%E9%9B%86%E6%88%90%E7%9A%84%E8%AB%8B%E6%B1%82%E5%BA%AB-546ec5424df9)
+
+### Share experience
+
+If you have alova experience worth sharing, or better practice cases, you can share it in [Github Discussion's Practices channel](https://github.com/alovajs/alova/discussions/categories/practices), better The sharing of will also be displayed in the official documentation.
+
+### Collaboration
+
+We welcome project collaboration with any organization or individual, which can help us expand alova's influence and accelerate the development of the project. If you have any suggestions or intentions for cooperation, please contact us via email **hujou555@gmail.com**.
+
+### Donation
+
+You can donate to the project through the following 3 ways. Please go to the donation page to view the donation privileges.
+
+1. [Github sponsors](https://github.com/sponsors/alovajs)
+2. [OpenCollective](https://opencollective.com/alova)
+3. [afdian](https://afdian.net/a/huzhen555)
+
+### Correct or add docs
+
+If you need to add new documentation content, or find errors in alova's documentation, such as wrong examples, wrong words, incorrect descriptions, or unmentioned content, you can [create a new document repository issue](https://github.com/alovajs/alovajs.github.io/issues/new), or [new document warehouse pull request](https://github.com/alovajs/alovajs.github.io/fork) directly modify the error, which should be the better option, and we welcome any suggestions or contributions to improve the documentation.
+
+### Translate Documentation
+
+If you are proficient in different languages, you are welcome to translate the alova documentation, which will help expand the use and audience of alova.
+
+## Become a core team member
+
+Check [HERE](./become-core-member) for details
diff --git a/versioned_docs/version-2.x/contributing/02-become-core-member.md b/versioned_docs/version-2.x/contributing/02-become-core-member.md
new file mode 100644
index 000000000..0ab350025
--- /dev/null
+++ b/versioned_docs/version-2.x/contributing/02-become-core-member.md
@@ -0,0 +1,31 @@
+---
+title: Become a core team member
+---
+
+🤝🤝🤝 If you also agree with alovajs’ philosophy, then let us create the next generation request library together!
+
+## Responsibilities
+
+1. Responsible for the development and maintenance of vscode plug-in projects, request scenario modules and adapter projects, as well as the Hongmeng version of alovajs.
+2. Write and optimize relevant development documents.
+3. In-depth participation in the entire life cycle of project design and implementation, promotion and maintenance.
+4. Participate in project development decision-making and commercialization exploration.
+
+## Revenue
+
+1. With the title of core member, you have the opportunity to enhance your reputation in the front-end field, thereby broadening your career development path.
+2. More opportunities for public sharing and opportunities to contact more front-end personnel.
+3. Broaden project thinking and technical experience through in-depth participation in project design, implementation and operation.
+4. Decision-making power on projects.
+5. Profit sharing.
+
+## Require
+
+1. Agree with alova’s design philosophy and development direction, and be willing to work hard to move forward together.
+2. Passion for technology, sense of responsibility, and teamwork.
+3. Technical capabilities only need to cover existing needs.
+4. Have some spare time.
+
+## How to join
+
+If you are interested, please send an email to [hujou555@gmail.com](mailto:hujou555@gmail.com), and be sure to add `alova core member application` at the beginning of email.
diff --git a/versioned_docs/version-2.x/contributing/03-developing-guidelines.md b/versioned_docs/version-2.x/contributing/03-developing-guidelines.md
new file mode 100644
index 000000000..19997d0a8
--- /dev/null
+++ b/versioned_docs/version-2.x/contributing/03-developing-guidelines.md
@@ -0,0 +1,137 @@
+---
+title: Developing Guidelines
+---
+
+:::info version required
+
+Node.js 16+, npm 8+
+
+:::
+
+## 1. Fork repository
+
+[Open alova fork page](https://github.com/alovajs/alova/fork), click "Create fork" and clone the forked repository to local.
+
+## 2. Clone to local
+
+Use the `git clone` command line, or the `Github Desktop` application to clone forked project.
+
+## 3. New pull request
+
+You can [create pull request through a forked repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) after writing code. You can also commit code in arbitrary batches, without commiting a complete code.
+
+## 4. Code something in your computer
+
+### Install dependencies
+
+Install dependencies using `npm install`.
+
+### Install the recommended plugin(vscode)
+
+If you are using vscode, it is recommended that you install the following plugins:
+
+- eslint: check code quality.
+- prettier: formatting code.
+- jest: Automatically execute unit test cases, and execute individual collection or unit test cases.
+- EditorConfig: Make sure the file format is consistent.
+
+### Project Structure
+
+```
+|-.github
+| |-ISSUE_TEMPLATE -> github issues template
+| |-workflows -> github action
+|-.husky -> husky configuration
+|-.vscode -> vscode configuration
+|-config -> rollup package files
+|-src -> source code
+|-test -> unit test suits
+| |-browser -> browser environment unit test suits
+| |-server -> SSR unit test suits
+| |-components -> Unit test components
+| |-mockServer.ts -> mock apis (msw)
+|-typings -> ts declaration
+|- Other configuration files
+
+```
+
+### Coding specifications
+
+#### Code format
+
+If you install the `prettier` plugin, it will automatically format codes every time you save files, so you don't have to worry about the format.
+
+#### Minimize code
+
+Lightweight is one of the alova's features, so it is necessary to minimize the amount of coding when coding. Here are a few coding specifications that need to be followed:
+
+1. Avoid the same code block, which can reduce the amount of code in the library, but two lines of code may not be worth encapsulating;
+2. Use a variable declarator to aggregate variable declarations, for example:
+
+```javascript
+// ❌
+const a = 1;
+const b = 2;
+
+// ✅
+const a = 1,
+ b = 2;
+```
+
+3. Use constants to save js built-in values and prototype methods to reduce the amount of code in the compilation phase of `uglify`. built-in values and prototype methods that often used are defined in `src/utils/variables.ts`.
+
+```javascript
+// ❌
+if (a === false) {
+ //...
+}
+arr.forEach(item => {
+ //...
+});
+
+// ✅
+import { falseValue, forEach } from '@/utils/variables';
+if (a === falseValue) {
+ //...
+}
+forEach(arr, item => {
+ //...
+});
+```
+
+## 5. Unit Testing Guidelines
+
+After finish code, it is necessary to add corresponding unit tests.
+
+The alova project uses **jest** as the unit test framework, and msw as the mock server. It is recommended to use the TDD mode. After modifying code every time, please pass the corresponding unit test.
+
+:::warning IMPORTANT
+
+When you're ready to commit your code, make sure all your unit tests are passed. When you're working on a pull request, you can have multiple small commits, and GitHub can automatically squash them before merging them.
+
+:::
+
+1. To add browser-related unit test cases, please add them to the corresponding test collection in `test/browser`, if there is no suitable test suits, you can create one by yourself;
+2. Add SSR-related unit test cases, please add them to the corresponding test collection in `test/server`, if there is no suitable test suits, you can create one by yourself;
+
+### Run and debug a single unit test or suits
+
+It is recommended to use the **jest** plugin (one of the plugins recommended above) to test a single use case or a suit. You can right-click the specified unit test, select `Run Test` to run it, and select `Debug Test` to debug it with breakpoint.
+
+![image](https://github.com/alovajs/alova/assets/29848971/a94ba9db-c100-472f-b870-6bcecb031bea)
+
+### Run all unit tests
+
+1. Use the **jest** plugin to run:
+
+![image](https://github.com/alovajs/alova/assets/29848971/5af3ff15-16b7-4b28-9ae6-d0b5a236b181)
+
+2. Run the browser unit tests with command line `npm run test:browser`, run the SSR unit tests with `npm run test:node`, and run both at the same time with `npm run test`.
+
+## 6. Commit codes
+
+alova uses [semantic-release](https://semantic-release.gitbook.io) as an automatic release tool, which can automatically release new version packages after merging code into `main` , and generate `CHANGELOG`, but you need to ensure that the committed message format follows [commit information convention](https://www.conventionalcommits.org/en/v1.0.0/), it is recommended that use `npm run commit` to automatically generate a git message that conforms to the specification.
+
+## 7. Writing docs
+
+If you are adding a new feature, you can try to add the relevant documentation of the new feature. For details, please read [Correcting or add docs](/contributing/overview#correct-or-add-docs), otherwise please explain it in the pull request.
diff --git a/versioned_docs/version-2.x/contributing/04-code-of-conduct.md b/versioned_docs/version-2.x/contributing/04-code-of-conduct.md
new file mode 100644
index 000000000..833a9ab0c
--- /dev/null
+++ b/versioned_docs/version-2.x/contributing/04-code-of-conduct.md
@@ -0,0 +1,136 @@
+---
+title: Code of Conduct
+---
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the overall
+ community
+
+Examples of unacceptable behavior include:
+
+- The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email address,
+ without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+[INSERT CONTACT METHOD].
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][mozilla coc].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][faq]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[mozilla coc]: https://github.com/mozilla/diversity
+[faq]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/versioned_docs/version-2.x/contributing/_category_.json b/versioned_docs/version-2.x/contributing/_category_.json
new file mode 100644
index 000000000..66f0e1824
--- /dev/null
+++ b/versioned_docs/version-2.x/contributing/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Contributing",
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/tutorial/01-example/01-init-page.md b/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md
similarity index 85%
rename from docs/tutorial/01-example/01-init-page.md
rename to versioned_docs/version-2.x/tutorial/01-example/01-init-page.md
index d38ee50b1..93bb52c2d 100644
--- a/docs/tutorial/01-example/01-init-page.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/01-init-page.md
@@ -1,16 +1,15 @@
----
-title: (vue)page initialization request
-sidebar_position: 10
----
-
-import InitPage from '@site/example-links/InitPage';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-This example mainly shows the simplicity of the request, it will return stateful request status, data, etc. which can be used directly in the view and managed directly by alova
-
-:::
+---
+title: page initialization request
+---
+
+import InitPage from '@site/example-links/InitPage';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+This example mainly shows the simplicity of the request, it will return stateful request status, data, etc. which can be used directly in the view and managed directly by alova
+
+:::
diff --git a/docs/tutorial/01-example/02-submit-form.md b/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md
similarity index 84%
rename from docs/tutorial/01-example/02-submit-form.md
rename to versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md
index b5f3bcce0..aa907fba6 100644
--- a/docs/tutorial/01-example/02-submit-form.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/02-submit-form.md
@@ -1,16 +1,15 @@
----
-title: (vue)form submission
-sidebar_position: 20
----
-
-import SubmitForm from '@site/example-links/SubmitForm';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-This example mainly shows the basic data submission encoding method
-
-:::
+---
+title: form submission
+---
+
+import SubmitForm from '@site/example-links/SubmitForm';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+This example mainly shows the basic data submission encoding method
+
+:::
diff --git a/docs/tutorial/01-example/03-condition-search.md b/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md
similarity index 89%
rename from docs/tutorial/01-example/03-condition-search.md
rename to versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md
index e7a58e069..d5a3696b5 100644
--- a/docs/tutorial/01-example/03-condition-search.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/03-condition-search.md
@@ -1,16 +1,15 @@
----
-title: (vue)Conditional Search
-sidebar_position: 30
----
-
-import ConditionSearch from '@site/example-links/ConditionSearch';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-This example mainly shows the simplicity of data search. There is no need for the developer to maintain the state of the request, data, etc., and there is no need to manually trigger the request to send. Just bind the state of the search conditions.
-
-:::
+---
+title: Conditional Search
+---
+
+import ConditionSearch from '@site/example-links/ConditionSearch';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+This example mainly shows the simplicity of data search. There is no need for the developer to maintain the state of the request, data, etc., and there is no need to manually trigger the request to send. Just bind the state of the search conditions.
+
+:::
diff --git a/docs/tutorial/01-example/04-memory-cache.md b/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md
similarity index 90%
rename from docs/tutorial/01-example/04-memory-cache.md
rename to versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md
index 6768af625..c26fd628f 100644
--- a/docs/tutorial/01-example/04-memory-cache.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/04-memory-cache.md
@@ -1,21 +1,20 @@
----
-title: (vue)Response Cache - Memory Mode
-sidebar_position: 40
----
-
-import MemoryCache from '@site/example-links/MemoryCache';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-The memory cache mode stores the response data in memory, and the cache is invalid when the page is refreshed.
-
-_Operation guide:_
-
-1. Click on the following student list item, the requesting student details will be sent, and the modal box will display the Loading status;
-2. Click the mask to close the pop-up box and reopen it. At this time, the cache will be hit and the student details will be displayed immediately, and the request record will no longer be printed in Request Records;
-
-:::
+---
+title: Response Cache - Memory Mode
+---
+
+import MemoryCache from '@site/example-links/MemoryCache';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+The memory cache mode stores the response data in memory, and the cache is invalid when the page is refreshed.
+
+_Operation guide:_
+
+1. Click on the following student list item, the requesting student details will be sent, and the modal box will display the Loading status;
+2. Click the mask to close the pop-up box and reopen it. At this time, the cache will be hit and the student details will be displayed immediately, and the request record will no longer be printed in Request Records;
+
+:::
diff --git a/docs/tutorial/01-example/05-storage-placeholder.md b/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md
similarity index 90%
rename from docs/tutorial/01-example/05-storage-placeholder.md
rename to versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md
index 9fd88b854..6ef9c18c8 100644
--- a/docs/tutorial/01-example/05-storage-placeholder.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/05-storage-placeholder.md
@@ -1,21 +1,20 @@
----
-title: (vue)Response Cache - Cache Placeholder Mode
-sidebar_position: 50
----
-
-import StoragePlaceholder from '@site/example-links/StoragePlaceholder';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-The cache placeholder mode is to persist the response data. It will be updated to the data state as placeholder data immediately after refreshing the page. At the same time, a request is sent. Developers can use placeholder data to replace the Loading state before responding.
-
-_Operation guide:_
-
-1. Click `Reload page` to refresh the page, you no longer see the Loading status, but the old data is rendered and replaced with new data when the request is responded;
-2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again;
-
-:::
+---
+title: Response Cache - Cache Placeholder Mode
+---
+
+import StoragePlaceholder from '@site/example-links/StoragePlaceholder';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+The cache placeholder mode is to persist the response data. It will be updated to the data state as placeholder data immediately after refreshing the page. At the same time, a request is sent. Developers can use placeholder data to replace the Loading state before responding.
+
+_Operation guide:_
+
+1. Click `Reload page` to refresh the page, you no longer see the Loading status, but the old data is rendered and replaced with new data when the request is responded;
+2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again;
+
+:::
diff --git a/docs/tutorial/01-example/06-storage-restore.md b/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md
similarity index 92%
rename from docs/tutorial/01-example/06-storage-restore.md
rename to versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md
index bfa105451..c81262f0a 100644
--- a/docs/tutorial/01-example/06-storage-restore.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/06-storage-restore.md
@@ -1,21 +1,20 @@
----
-title: (vue)Response Caching - Restore Mode
-sidebar_position: 60
----
-
-import StorageRestore from '@site/example-links/StorageRestore';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-The cache restore mode is to persist the response data. When the request hits the cache, the persistent cached data will be returned, and no more requests will be issued. It is generally used for some data that needs server management but does not change for a certain period of time. The following is an example of the restore mode of holiday information.
-
-_Operation guide:_
-
-1. Click `Reload page` to refresh the page, you will no longer see the Loading status, but will use the cached data and render it to the page immediately, and no longer send requests;
-2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again;
-
-:::
+---
+title: Response Caching - Restore Mode
+---
+
+import StorageRestore from '@site/example-links/StorageRestore';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+The cache restore mode is to persist the response data. When the request hits the cache, the persistent cached data will be returned, and no more requests will be issued. It is generally used for some data that needs server management but does not change for a certain period of time. The following is an example of the restore mode of holiday information.
+
+_Operation guide:_
+
+1. Click `Reload page` to refresh the page, you will no longer see the Loading status, but will use the cached data and render it to the page immediately, and no longer send requests;
+2. Click `Invalidate the data of placeholder` to invalidate the cached data, then you will see the Loading status again;
+
+:::
diff --git a/docs/tutorial/01-example/07-update-state.md b/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md
similarity index 86%
rename from docs/tutorial/01-example/07-update-state.md
rename to versioned_docs/version-2.x/tutorial/01-example/07-update-state.md
index 00f6ed794..50addbe55 100644
--- a/docs/tutorial/01-example/07-update-state.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/07-update-state.md
@@ -1,20 +1,19 @@
----
-title: (vue)Update state across components/pages
-sidebar_position: 70
----
-
-import UpdateState from '@site/example-links/UpdateState';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-When you need to update state across components or pages, you are often limited by the component hierarchy. Here is a way to break this limitation.
-
-_Operation guidance:_
-
-In the edit box or edit page, after adding or editing list data, the list data is updated across component levels.
-
-:::
+---
+title: Update state across components/pages
+---
+
+import UpdateState from '@site/example-links/UpdateState';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+When you need to update state across components or pages, you are often limited by the component hierarchy. Here is a way to break this limitation.
+
+_Operation guidance:_
+
+In the edit box or edit page, after adding or editing list data, the list data is updated across component levels.
+
+:::
diff --git a/docs/tutorial/01-example/08-prefetch.md b/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md
similarity index 90%
rename from docs/tutorial/01-example/08-prefetch.md
rename to versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md
index ab2690dc1..8428f1ca1 100644
--- a/docs/tutorial/01-example/08-prefetch.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/08-prefetch.md
@@ -1,21 +1,20 @@
----
-title: (vue)Data Prefetch
-sidebar_position: 80
----
-
-import Prefetch from '@site/example-links/Prefetch';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-Data pre-fetching is a way to predict user operation behavior to achieve a strategy of displaying content to users faster.
-
-_Operation guide:_
-
-1. Move the mouse to any list item and stay there for 0.2 seconds, a request for detailed data will be sent on the bottom panel;
-2. Click to open this list item, you can see the detailed data immediately;
-
-:::
+---
+title: Data Prefetch
+---
+
+import Prefetch from '@site/example-links/Prefetch';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+Data pre-fetching is a way to predict user operation behavior to achieve a strategy of displaying content to users faster.
+
+_Operation guide:_
+
+1. Move the mouse to any list item and stay there for 0.2 seconds, a request for detailed data will be sent on the bottom panel;
+2. Click to open this list item, you can see the detailed data immediately;
+
+:::
diff --git a/docs/tutorial/01-example/09-load-more.md b/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md
similarity index 93%
rename from docs/tutorial/01-example/09-load-more.md
rename to versioned_docs/version-2.x/tutorial/01-example/09-load-more.md
index ebe27569f..048739832 100644
--- a/docs/tutorial/01-example/09-load-more.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/09-load-more.md
@@ -1,23 +1,22 @@
----
-title: (vue)Load more
-sidebar_position: 90
----
-
-import LoadMore from '@site/example-links/LoadMore';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization.
-
-_Operation guide:_
-
-1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait for scrolling down the page;
-2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request;
-
-[usePagination documentation](/tutorial/strategy/usePagination)
-
-:::
+---
+title: Load more
+---
+
+import LoadMore from '@site/example-links/LoadMore';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization.
+
+_Operation guide:_
+
+1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait for scrolling down the page;
+2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request;
+
+[usePagination documentation](/tutorial/strategy/usePagination)
+
+:::
diff --git a/docs/tutorial/01-example/10-paginated-list.md b/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md
similarity index 93%
rename from docs/tutorial/01-example/10-paginated-list.md
rename to versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md
index a1e55d20d..88cfcfd4c 100644
--- a/docs/tutorial/01-example/10-paginated-list.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/10-paginated-list.md
@@ -1,23 +1,22 @@
----
-title: (vue)Paginated list
-sidebar_position: 100
----
-
-import Pagination from '@site/example-links/Pagination';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
-
-
-
-:::info example description
-
-Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization.
-
-_Operation guide:_
-
-1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait when turning to the next page;
-2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request;
-
-[usePagination documentation](/tutorial/strategy/usePagination)
-
-:::
+---
+title: Paginated list
+---
+
+import Pagination from '@site/example-links/Pagination';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte. For details, please read the [Getting Started Guide](/tutorial/getting-started);
+
+
+
+:::info example description
+
+Using pagination strategy, achieve a more high-performance and easy-to-use pagination function, automatic management of paging-related status, preloading of previous and previous pages, and automatic maintenance of data addition/editing/replacement/ Removed, and request-level stabilization.
+
+_Operation guide:_
+
+1. After the initialization is completed, the next page of data will be preloaded, and there is no need to wait when turning to the next page;
+2. Adding, deleting, and modifying list items does not need to reset the list, it will be automatically processed into the same effect as the re-request;
+
+[usePagination documentation](/tutorial/strategy/usePagination)
+
+:::
diff --git a/docs/tutorial/01-example/11-controlled-cache-by-indexeddb.md b/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md
similarity index 91%
rename from docs/tutorial/01-example/11-controlled-cache-by-indexeddb.md
rename to versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md
index b5b5eaf4f..5ebbc47f2 100644
--- a/docs/tutorial/01-example/11-controlled-cache-by-indexeddb.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/11-controlled-cache-by-indexeddb.md
@@ -1,23 +1,22 @@
----
-title: (vue)Manage Cache with IndexedDB
-sidebar_position: 110
----
-
-import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-Using controlled cache allows developers to customize and manage the cache. Under the large file cache, it can cooperate with IndexedDB to manage the local cache.
-
-_Operation guidance:_
-
-1. Select one of the pictures, the picture will request the network to load first, and the picture data will be saved in the local IndexedDB;
-2. Refresh the page and select the same picture again, the picture will get data from IndexedDB instead of initiating a network request;
-
-[Controlled cache document](/tutorial/cache/controlled-cache)
-
-:::
+---
+title: Manage Cache with IndexedDB
+---
+
+import ControlledCacheByIndexedDB from '@site/example-links/ControlledCacheByIndexedDB';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+Using controlled cache allows developers to customize and manage the cache. Under the large file cache, it can cooperate with IndexedDB to manage the local cache.
+
+_Operation guidance:_
+
+1. Select one of the pictures, the picture will request the network to load first, and the picture data will be saved in the local IndexedDB;
+2. Refresh the page and select the same picture again, the picture will get data from IndexedDB instead of initiating a network request;
+
+[Controlled cache document](/tutorial/cache/controlled-cache)
+
+:::
diff --git a/docs/tutorial/01-example/12-silent-submit-setting.md b/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md
similarity index 90%
rename from docs/tutorial/01-example/12-silent-submit-setting.md
rename to versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md
index aa49e9dc2..6d2f1215e 100644
--- a/docs/tutorial/01-example/12-silent-submit-setting.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/12-silent-submit-setting.md
@@ -1,23 +1,22 @@
----
-title: (svelte)Silent Submit - Settings Page
-sidebar_position: 120
----
-
-import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte';
-
-> The example uses svelte as an example, but you can also use alova in react and vue, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-Use the silent submission strategy, submit and respond immediately, greatly reducing the impact of network fluctuations, so that your application is still very smooth even when the network is unstable or even disconnected.
-
-_Operation guidance:_
-
-1. Operate the setting item, it will generate feedback immediately without waiting for the server to respond;
-2. Switch the request mode and network status to experience the difference between them;
-
-[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction)
-
-:::
+---
+title: Silent Submit - Settings Page
+---
+
+import SettingSilentSvelte from '@site/example-links/SettingSilentSvelte';
+
+> The example uses svelte as an example, but you can also use alova in react and vue, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+Use the silent submission strategy, submit and respond immediately, greatly reducing the impact of network fluctuations, so that your application is still very smooth even when the network is unstable or even disconnected.
+
+_Operation guidance:_
+
+1. Operate the setting item, it will generate feedback immediately without waiting for the server to respond;
+2. Switch the request mode and network status to experience the difference between them;
+
+[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction)
+
+:::
diff --git a/docs/tutorial/01-example/13-silent-submit-simple-list.md b/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md
similarity index 91%
rename from docs/tutorial/01-example/13-silent-submit-simple-list.md
rename to versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md
index 2b4ba3046..be1df2877 100644
--- a/docs/tutorial/01-example/13-silent-submit-simple-list.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/13-silent-submit-simple-list.md
@@ -1,23 +1,22 @@
----
-title: (vue)Silent submit - Simple List
-sidebar_position: 130
----
-
-import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected.
-
-_Operation guidance:_
-
-1. Add, edit, and delete list items, it will generate feedback immediately without waiting for the server to respond;
-2. Switch the request mode and network status to experience the difference between them;
-
-[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction)
-
-:::
+---
+title: Silent submit - Simple List
+---
+
+import SimpleListSilentVue from '@site/example-links/SimpleListSilentVue';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected.
+
+_Operation guidance:_
+
+1. Add, edit, and delete list items, it will generate feedback immediately without waiting for the server to respond;
+2. Switch the request mode and network status to experience the difference between them;
+
+[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction)
+
+:::
diff --git a/docs/tutorial/01-example/14-silent-submit-notes.md b/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md
similarity index 91%
rename from docs/tutorial/01-example/14-silent-submit-notes.md
rename to versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md
index d4ae8b3b8..acdddbb66 100644
--- a/docs/tutorial/01-example/14-silent-submit-notes.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/14-silent-submit-notes.md
@@ -1,23 +1,22 @@
----
-title: (react)Silent submit - Notebook
-sidebar_position: 140
----
-
-import NoteSilentReact from '@site/example-links/NoteSilentReact';
-
-> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected.
-
-_Operation guidance:_
-
-1. Add, edit, and delete notes, it will generate feedback immediately without waiting for the server to respond;
-2. Switch the request mode and network status to experience the difference between them;
-
-[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction)
-
-:::
+---
+title: Silent submit - Notebook
+---
+
+import NoteSilentReact from '@site/example-links/NoteSilentReact';
+
+> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+A simple list implemented using the silent submission strategy, which responds immediately after submission, greatly reduces the impact of network fluctuations, allowing your application to remain very smooth even when the network is unstable or even disconnected.
+
+_Operation guidance:_
+
+1. Add, edit, and delete notes, it will generate feedback immediately without waiting for the server to respond;
+2. Switch the request mode and network status to experience the difference between them;
+
+[Silent submit strategy document](/tutorial/strategy/sensorless-data-interaction)
+
+:::
diff --git a/docs/tutorial/01-example/15-form-hook.md b/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md
similarity index 86%
rename from docs/tutorial/01-example/15-form-hook.md
rename to versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md
index 8dbccd923..6fc59d261 100644
--- a/docs/tutorial/01-example/15-form-hook.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/15-form-hook.md
@@ -1,18 +1,17 @@
----
-title: (react)Form submit strategy
-sidebar_position: 150
----
-
-import FormHook from '@site/example-links/FormHook';
-
-> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately.
-
-[Form submit strategy document](/tutorial/strategy/useForm)
-
-:::
+---
+title: Form submit strategy
+---
+
+import FormHook from '@site/example-links/FormHook';
+
+> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately.
+
+[Form submit strategy document](/tutorial/strategy/useForm)
+
+:::
diff --git a/docs/tutorial/01-example/16-captcha-send.md b/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md
similarity index 87%
rename from docs/tutorial/01-example/16-captcha-send.md
rename to versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md
index 1eca34a3d..68ce05a32 100644
--- a/docs/tutorial/01-example/16-captcha-send.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/16-captcha-send.md
@@ -1,18 +1,17 @@
----
-title: (react)Send captcha
-sidebar_position: 160
----
-
-import CaptchaSend from '@site/example-links/CaptchaSend';
-
-> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-This example demonstrates the convenient implementation of the captcha sending function.
-
-[Captcha sending strategy document](/tutorial/strategy/useCaptcha)
-
-:::
+---
+title: Send captcha
+---
+
+import CaptchaSend from '@site/example-links/CaptchaSend';
+
+> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+This example demonstrates the convenient implementation of the captcha sending function.
+
+[Captcha sending strategy document](/tutorial/strategy/useCaptcha)
+
+:::
diff --git a/docs/tutorial/01-example/17-retriable-hook.md b/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md
similarity index 86%
rename from docs/tutorial/01-example/17-retriable-hook.md
rename to versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md
index 8febc2043..eaf6d19e4 100644
--- a/docs/tutorial/01-example/17-retriable-hook.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/17-retriable-hook.md
@@ -1,18 +1,17 @@
----
-title: (react)Request Retry/Poll Request
-sidebar_position: 170
----
-
-import RetriableHook from '@site/example-links/RetriableHook';
-
-> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately.
-
-[Request Retry Strategy Documentation](/tutorial/strategy/useRetriableRequest)
-
-:::
+---
+title: Request Retry/Poll Request
+---
+
+import RetriableHook from '@site/example-links/RetriableHook';
+
+> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+This example provides four parts: form data persistence, form editing, multi-module form, and form filtering data, which can be tried separately.
+
+[Request Retry Strategy Documentation](/tutorial/strategy/useRetriableRequest)
+
+:::
diff --git a/docs/tutorial/01-example/18.action-delegation-middleware.md b/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md
similarity index 87%
rename from docs/tutorial/01-example/18.action-delegation-middleware.md
rename to versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md
index 2590d7c38..a6d704994 100644
--- a/docs/tutorial/01-example/18.action-delegation-middleware.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/18.action-delegation-middleware.md
@@ -1,18 +1,17 @@
----
-title: (react)Trigger requests across components
-sidebar_position: 180
----
-
-import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware';
-
-> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-This example mainly uses the `actionDelegationMiddleware` middleware to demonstrate the refresh of server-side data across any level of components without using global state management.
-
-[Actions delegation middleware document](/tutorial/strategy/actionDelegationMiddleware)
-
-:::
+---
+title: Trigger requests across components
+---
+
+import ActionDelegationMiddleware from '@site/example-links/ActionDelegationMiddleware';
+
+> The example uses react as an example, but you can also use alova in vue3 and svelte, please read the [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+This example mainly uses the `actionDelegationMiddleware` middleware to demonstrate the refresh of server-side data across any level of components without using global state management.
+
+[Actions delegation middleware document](/tutorial/strategy/actionDelegationMiddleware)
+
+:::
diff --git a/docs/tutorial/01-example/19-serial-request.md b/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md
similarity index 87%
rename from docs/tutorial/01-example/19-serial-request.md
rename to versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md
index 34fb885df..0203ca512 100644
--- a/docs/tutorial/01-example/19-serial-request.md
+++ b/versioned_docs/version-2.x/tutorial/01-example/19-serial-request.md
@@ -1,19 +1,18 @@
----
-title: (vue)serial request hook
-sidebar_position: 190
----
-
-import SerialRequest from '@site/example-links/SerialRequest';
-
-> The example uses vue3 as an example, but you can also use alova in react and svelte, please read [Getting Started Guide](/tutorial/getting-started) for details;
-
-
-
-:::info example description
-
-This example mainly demonstrates a more elegant serial request through `useSerialRequest`.
-
-[useSerialRequest](/tutorial/strategy/useSerialRequest)
-[useSerialWatcher](/tutorial/strategy/useSerialWatcher)
-
-:::
+---
+title: serial request hook
+---
+
+import SerialRequest from '@site/example-links/SerialRequest';
+
+> The example uses vue3 as an example, but you can also use alova in react and svelte, please read [Getting Started Guide](/tutorial/getting-started) for details;
+
+
+
+:::info example description
+
+This example mainly demonstrates a more elegant serial request through `useSerialRequest`.
+
+[useSerialRequest](/tutorial/strategy/useSerialRequest)
+[useSerialWatcher](/tutorial/strategy/useSerialWatcher)
+
+:::
diff --git a/docs/tutorial/01-example/_category_.json b/versioned_docs/version-2.x/tutorial/01-example/_category_.json
similarity index 97%
rename from docs/tutorial/01-example/_category_.json
rename to versioned_docs/version-2.x/tutorial/01-example/_category_.json
index 52fa495e9..82e0066dc 100644
--- a/docs/tutorial/01-example/_category_.json
+++ b/versioned_docs/version-2.x/tutorial/01-example/_category_.json
@@ -1,7 +1,7 @@
-{
- "label": "Examples",
- "link": {
- "type": "generated-index",
- "description": "Here are rich examples to show how alova behaves in different request scenarios.The dependencies will be installed before the example runs, so wait for a while!"
- }
-}
+{
+ "label": "Examples",
+ "link": {
+ "type": "generated-index",
+ "description": "Here are rich examples to show how alova behaves in different request scenarios.The dependencies will be installed before the example runs, so wait for a while!"
+ }
+}
diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md b/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md
new file mode 100644
index 000000000..ec9ead664
--- /dev/null
+++ b/versioned_docs/version-2.x/tutorial/02-getting-started/02-quick-start.md
@@ -0,0 +1,114 @@
+---
+title: Quick Start
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import EmbedSandpack from "@site/src/components/EmbedSandpack";
+
+import quickStartGET from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/get.js';
+import quickStartPOST from '!!raw-loader!@site/codesandbox/01-getting-started/02-first-request/post.js';
+
+:::tip Example tip
+
+If you haven’t learned about alova yet, it is recommended that you read [alova overview](/tutorial/getting-started) first.
+
+:::
+
+## Install
+
+
+
+
+```bash
+npm install alova --save
+```
+
+
+
+
+```bash
+yarn add alova
+```
+
+
+
+
+```bash
+pnpm add alova
+```
+
+
+
+
+```bash
+bun add alova
+```
+
+
+
+
+> You can also [use alova through CDN](/tutorial/others/use-in-static)
+
+## Create alova instance
+
+In alova, a request needs to be made through an alova instance. Let's create one first. When creating an alova instance, you need to specify a request adapter. It is recommended to use the `GlobalFetch` request adapter here, which is a package based on the `fetch API`.
+
+
+
+
+```javascript
+import { createAlova } from 'alova';
+import GlobalFetch from 'alova/GlobalFetch';
+
+const alovaInstance = createAlova({
+ requestAdapter: GlobalFetch()
+});
+```
+
+
+
+
+```javascript
+const { createAlova } = require('alova');
+const GlobalFetch = require('alova/GlobalFetch');
+
+const alova = createAlova({
+ requestAdapter: GlobalFetch();
+});
+```
+
+> When using GlobalFetch in nodejs, the nodejs version requires `v17.5`, or you can use [axios request adapter](/tutorial/request-adapter/alova-adapter-axios/).
+
+
+
+
+```javascript
+import { createAlova } from 'npm:alova';
+import GlobalFetch from 'npm:alova/GlobalFetch';
+
+const alova = createAlova({
+ requestAdapter: GlobalFetch();
+});
+```
+
+
+
+
+## GET request
+
+Sending a request via `alovaInstance.Get` will receive a `Response` instance thanks to the `GlobalFetch` request adapter, which is simple.
+
+
+
+In an asynchronous function, you can also use `await alovaInstance.Get` to wait for a response.
+
+## POST request
+
+Submitting data via `alovaInstance.Post` is also easy.
+
+
+
+## What’s next?
+
+In fact, this is just the simplest request example. We will learn more about the features in the next chapters, so let's start learning.
diff --git a/docs/tutorial/02-getting-started/03-method.md b/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md
similarity index 96%
rename from docs/tutorial/02-getting-started/03-method.md
rename to versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md
index a188cbd1d..de747da4f 100644
--- a/docs/tutorial/02-getting-started/03-method.md
+++ b/versioned_docs/version-2.x/tutorial/02-getting-started/03-method.md
@@ -1,306 +1,305 @@
----
-title: Method Instance
-sidebar_position: 30
----
-
-In the previous chapter we tried to send a request and obtain the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can use `then, catch, finally` methods or `await` Send the request just like a Promise object.
-
-```javascript
-const userMethodInstance = alovaInstance.Get('/api/user');
-
-userMethodInstance.then(response => {
- // ...
-});
-
-userMethodInstance.catch(error => {
- // ...
-});
-
-userMethodInstance.finally(() => {
- // ...
-});
-
-try {
- await userMethodInstance;
-} catch (error) {
- // ...
-} finally {
- // ...
-}
-```
-
-Simple way to write:
-
-```javascript
-const response = await alovaInstance.Get('/api/user');
-```
-
-Each method instance describes the type of each request, request url, request headers, request parameters, etc. In addition, you can define request behavior on the method instance to control how the method handles the request.
-
-## Request type
-
-alova provides a total of 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH.
-
-| Instance creation function | Parameters |
-| -------------------------- | --------------------------------------------- |
-| GET | `alovaInstance.Get(url[, config])` |
-| POST | `alovaInstance.Post(url[, data[, config]])` |
-| PUT | `alovaInstance.Put(url[, data[, config]])` |
-| DELETE | `alovaInstance.Delete(url[, data[, config]])` |
-| HEAD | `alovaInstance.Head(url[, config])` |
-| OPTIONS | `alovaInstance.Options(url[, config])` |
-| PATCH | `alovaInstance.Patch(url[, data[, config]])` |
-
-Parameter Description:
-
-- `url` is the request path;
-- `data` is the request body data;
-- `config` is the request configuration object, which includes configurations such as request headers, params parameters, request behavior parameters, etc.;
-
-you can also create a method instance customly. This is useful when you need to dynamically specify the request type.
-
-```javascript
-import { Method } from 'alova';
-
-const method = new Method('GET', alovaInstance, '/api/users', {
- params: {
- ID: 1
- }
-});
-```
-
-Next, let’s take a look at how to define request parameters, which should seem familiar to you.
-
-## Request parameters
-
-### URL parameters
-
-Pass in the URL parameters through params, and the params parameters will be spliced in the form of ? after the url.
-
-```javascript
-alovaInstance.Get('/todo/list', {
- params: {
- userId: 1
- }
-});
-```
-
-Of course, you can also directly splice it behind the url, and the effect will be the same.
-
-```javascript
-alovaInstance.Get('/todo/list?userId=1');
-```
-
-### Request body
-
-When sending **POST, PUT, DELETE, PATCH request**, you can send data through the request body. At this time, the second parameter is passed in the request body. It is worth noting that the POST request can also pass in the params parameter.
-
-```javascript
-alovaInstance.Post(
- '/todo',
- //The second parameter is the request body
- {
- title: 'test todo',
- time: '12:00'
- },
- // The third parameter is configuration
- {
- params: {
- userId: 1
- }
- }
-);
-```
-
-### Request header
-
-Specify request headers via headers.
-
-```javascript
-alovaInstance.Get('/user', {
- headers: {
- 'Content-Type': 'application/json;charset=UTF-8'
- }
-});
-```
-
-### Other parameters supported by the request adapter
-
-In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `GlobalFetch` as the request adapter of alova, you can configure any `fetch API` supported parameters on the `method` instance. These Parameters will be passed to the `fetch` function during request.
-
-```javascript
-alovaInstance.Get('/todo/list', {
- // ...
- // highlight-start
- credentials: 'same-origin',
- referrerPolicy: 'no-referrer',
- mode: 'cors'
- // highlight-end
-});
-```
-
-When the above `method` instance sends a request through `fetch`, it will be requested with the following parameters.
-
-```javascript
-fetch('/todo/list', {
- // ...
- // highlight-start
- credentials: 'same-origin',
- referrerPolicy: 'no-referrer',
- mode: 'cors'
- // highlight-end
-});
-```
-
-> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter. For example, GlobalFetch supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters.
-
-If you use other request adapters, you can also pass the parameters they support.
-
-## Request behavior
-
-In [RSM](/tutorial/others/RSM), request behavior is used to describe how the request will be handled.
-
-### overtime time
-
-Set request timeout.
-
-```javascript
-// Request timeout at request level
-alovaInstance.Get('/todo/list', {
- // ...
- // highlight-start
- timeout: 10000
- // highlight-end
-});
-```
-
-### Request sharing
-
-We will always encounter this situation. When a request is issued but has not yet been responded to, the same request is issued again, resulting in a waste of requests or repeated submission of problems, such as the following three scenarios:
-
-1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be issued at the same time;
-2. The submit button is not disabled and the user clicks the submit button multiple times;
-3. When the preloading page is entered before preloading is completed, multiple identical requests will be initiated;
-4. Prevent repeated requests from being sent under react’s StrictMode;
-
-Shared requests are used to solve these problems. It can not only improve application fluency, but also reduce server pressure.
-
-```mermaid
-flowchart LR
- classDef response fill:#a8bcff
- R1[Request 1] --> S1[Send request] --> W1[Waiting for response]:::response --> RE1[Receive data 1]
- R2[Same request as request 1] --> W1[Waiting for response]:::response --> RE2[Receive data 1]
-```
-
-Request sharing is enabled by default. If you wish to turn off request sharing on a specific request, you can do this:
-
-```javascript
-alovaInst.Get('/todo', {
- // ...
- // highlight-start
- shareRequest: false
- // highlight-end
-});
-```
-
-:::warning How to identify identical requests
-
-The request method, request URL, request header, URL parameters, and request body of the method instance are used as a unique identifier. If the identifier is the same, it means the same request, instead of comparing the reference address of the method instance.
-
-:::
-
-### Convert response data
-
-Sometimes we need to uniformly transform the response data. We can set the `transformData` function for the method instance to convert the response data into the required structure.
-
-```javascript
-alovaInstance.Get('/todo/list', {
- // The function accepts response data and response header data, and requires the converted data to be returned.
- transformData(rawData, headers) {
- return rawData.list.map(item => {
- return {
- ...item,
- statusText: item.done ? 'Completed' : 'In progress'
- };
- });
- }
-});
-```
-
-### Response cache
-
-Response caching allows you to better utilize server-side data multiple times without having to send a request to obtain the data every time it is requested. GET requests will set a memory cache time of 5 minutes by default, which we will explain in detail in the [Response Caching](/tutorial/cache/mode) section later.
-
-## Abort request
-
-`[2.6.2+]` calls the `abort` of the method instance to abort the request.
-
-```javascript
-const userMethod = alovaInstance.Get('/api/user');
-userMethod.then(res => {
- // ...
-});
-
-const handleCancel = () => {
- // highlight-start
- userMethod.abort();
- // highlight-end
-};
-```
-
-## Monitor upload and download progress
-
-**[v2.17.0+]** Bind the upload progress event through `onUpload` of the method instance, and bind the download progress event through `onDownload`, which will return the unbinding function.
-
-```javascript
-const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData);
-const offUploadEvent = uploadMethod.onUpload(event => {
- console.log('File size:', event.total);
- console.log('Uploaded:', event.loaded);
-});
-
-uploadMethod.then(res => {
- // ...
-});
-
-// Unbind upload callback
-const handleOffEvent = () => {
- offUploadEvent();
-};
-```
-
-```javascript
-const downloadMethod = alovaInstance.Get('/todo/downloadfile');
-const offDownloadEvent = downloadMethod.onDownload(event => {
- console.log('File size:', event.total);
- console.log('Downloaded:', event.loaded);
-});
-
-downloadMethod.then(res => {
- // ...
-});
-
-// Unbind download callback
-const handleOffEvent = () => {
- offDownloadEvent();
-};
-```
-
-:::warning Things to note when using the `GlobalFetch` adapter
-
-Due to fetch api limitations, the **GlobalFetch** adapter provided by alova does not support upload progress. If you need to upload progress, please use [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) or [axios Adapter](/tutorial/request-adapter/alova-adapter-axios).
-
-You can also write your own request adapter, see [Writing Request Adapter](/tutorial/custom/custom-http-adapter) for details.
-
-:::
-
-**Upload/Download Status Type**
-
-```typescript
-type Progress = {
- /** Total amount of data uploaded or downloaded */
- total: number;
- /** Completed data */
- loaded: number;
-};
-```
+---
+title: Method Instance
+---
+
+In the previous chapter we tried to send a request and obtain the response data. In fact, `alovaInstance.Get(...)` is not a function that initiates a request, but creates a method instance, which is a PromiseLike instance. You can use `then, catch, finally` methods or `await` Send the request just like a Promise object.
+
+```javascript
+const userMethodInstance = alovaInstance.Get('/api/user');
+
+userMethodInstance.then(response => {
+ // ...
+});
+
+userMethodInstance.catch(error => {
+ // ...
+});
+
+userMethodInstance.finally(() => {
+ // ...
+});
+
+try {
+ await userMethodInstance;
+} catch (error) {
+ // ...
+} finally {
+ // ...
+}
+```
+
+Simple way to write:
+
+```javascript
+const response = await alovaInstance.Get('/api/user');
+```
+
+Each method instance describes the type of each request, request url, request headers, request parameters, etc. In addition, you can define request behavior on the method instance to control how the method handles the request.
+
+## Request type
+
+alova provides a total of 7 request types: GET, POST, PUT, DELETE, HEAD, OPTIONS, and PATCH.
+
+| Instance creation function | Parameters |
+| -------------------------- | --------------------------------------------- |
+| GET | `alovaInstance.Get(url[, config])` |
+| POST | `alovaInstance.Post(url[, data[, config]])` |
+| PUT | `alovaInstance.Put(url[, data[, config]])` |
+| DELETE | `alovaInstance.Delete(url[, data[, config]])` |
+| HEAD | `alovaInstance.Head(url[, config])` |
+| OPTIONS | `alovaInstance.Options(url[, config])` |
+| PATCH | `alovaInstance.Patch(url[, data[, config]])` |
+
+Parameter Description:
+
+- `url` is the request path;
+- `data` is the request body data;
+- `config` is the request configuration object, which includes configurations such as request headers, params parameters, request behavior parameters, etc.;
+
+you can also create a method instance customly. This is useful when you need to dynamically specify the request type.
+
+```javascript
+import { Method } from 'alova';
+
+const method = new Method('GET', alovaInstance, '/api/users', {
+ params: {
+ ID: 1
+ }
+});
+```
+
+Next, let’s take a look at how to define request parameters, which should seem familiar to you.
+
+## Request parameters
+
+### URL parameters
+
+Pass in the URL parameters through params, and the params parameters will be spliced in the form of ? after the url.
+
+```javascript
+alovaInstance.Get('/todo/list', {
+ params: {
+ userId: 1
+ }
+});
+```
+
+Of course, you can also directly splice it behind the url, and the effect will be the same.
+
+```javascript
+alovaInstance.Get('/todo/list?userId=1');
+```
+
+### Request body
+
+When sending **POST, PUT, DELETE, PATCH request**, you can send data through the request body. At this time, the second parameter is passed in the request body. It is worth noting that the POST request can also pass in the params parameter.
+
+```javascript
+alovaInstance.Post(
+ '/todo',
+ //The second parameter is the request body
+ {
+ title: 'test todo',
+ time: '12:00'
+ },
+ // The third parameter is configuration
+ {
+ params: {
+ userId: 1
+ }
+ }
+);
+```
+
+### Request header
+
+Specify request headers via headers.
+
+```javascript
+alovaInstance.Get('/user', {
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ }
+});
+```
+
+### Other parameters supported by the request adapter
+
+In addition to request headers, params parameters, etc., it also supports configuring parameters supported by the corresponding request adapter. When using `GlobalFetch` as the request adapter of alova, you can configure any `fetch API` supported parameters on the `method` instance. These Parameters will be passed to the `fetch` function during request.
+
+```javascript
+alovaInstance.Get('/todo/list', {
+ // ...
+ // highlight-start
+ credentials: 'same-origin',
+ referrerPolicy: 'no-referrer',
+ mode: 'cors'
+ // highlight-end
+});
+```
+
+When the above `method` instance sends a request through `fetch`, it will be requested with the following parameters.
+
+```javascript
+fetch('/todo/list', {
+ // ...
+ // highlight-start
+ credentials: 'same-origin',
+ referrerPolicy: 'no-referrer',
+ mode: 'cors'
+ // highlight-end
+});
+```
+
+> In addition to passing Object, the request body can also pass request body parameters supported by the request adapter. For example, GlobalFetch supports passing `string | FormData | Blob | ArrayBuffer | URLSearchParams | ReadableStream` parameters.
+
+If you use other request adapters, you can also pass the parameters they support.
+
+## Request behavior
+
+In [RSM](/tutorial/others/RSM), request behavior is used to describe how the request will be handled.
+
+### overtime time
+
+Set request timeout.
+
+```javascript
+// Request timeout at request level
+alovaInstance.Get('/todo/list', {
+ // ...
+ // highlight-start
+ timeout: 10000
+ // highlight-end
+});
+```
+
+### Request sharing
+
+We will always encounter this situation. When a request is issued but has not yet been responded to, the same request is issued again, resulting in a waste of requests or repeated submission of problems, such as the following three scenarios:
+
+1. A component will obtain initialization data when it is created. When a page renders multiple components at the same time, multiple identical requests will be issued at the same time;
+2. The submit button is not disabled and the user clicks the submit button multiple times;
+3. When the preloading page is entered before preloading is completed, multiple identical requests will be initiated;
+4. Prevent repeated requests from being sent under react’s StrictMode;
+
+Shared requests are used to solve these problems. It can not only improve application fluency, but also reduce server pressure.
+
+```mermaid
+flowchart LR
+ classDef response fill:#a8bcff
+ R1[Request 1] --> S1[Send request] --> W1[Waiting for response]:::response --> RE1[Receive data 1]
+ R2[Same request as request 1] --> W1[Waiting for response]:::response --> RE2[Receive data 1]
+```
+
+Request sharing is enabled by default. If you wish to turn off request sharing on a specific request, you can do this:
+
+```javascript
+alovaInst.Get('/todo', {
+ // ...
+ // highlight-start
+ shareRequest: false
+ // highlight-end
+});
+```
+
+:::warning How to identify identical requests
+
+The request method, request URL, request header, URL parameters, and request body of the method instance are used as a unique identifier. If the identifier is the same, it means the same request, instead of comparing the reference address of the method instance.
+
+:::
+
+### Convert response data
+
+Sometimes we need to uniformly transform the response data. We can set the `transformData` function for the method instance to convert the response data into the required structure.
+
+```javascript
+alovaInstance.Get('/todo/list', {
+ // The function accepts response data and response header data, and requires the converted data to be returned.
+ transformData(rawData, headers) {
+ return rawData.list.map(item => {
+ return {
+ ...item,
+ statusText: item.done ? 'Completed' : 'In progress'
+ };
+ });
+ }
+});
+```
+
+### Response cache
+
+Response caching allows you to better utilize server-side data multiple times without having to send a request to obtain the data every time it is requested. GET requests will set a memory cache time of 5 minutes by default, which we will explain in detail in the [Response Caching](/tutorial/cache/mode) section later.
+
+## Abort request
+
+`[2.6.2+]` calls the `abort` of the method instance to abort the request.
+
+```javascript
+const userMethod = alovaInstance.Get('/api/user');
+userMethod.then(res => {
+ // ...
+});
+
+const handleCancel = () => {
+ // highlight-start
+ userMethod.abort();
+ // highlight-end
+};
+```
+
+## Monitor upload and download progress
+
+**[v2.17.0+]** Bind the upload progress event through `onUpload` of the method instance, and bind the download progress event through `onDownload`, which will return the unbinding function.
+
+```javascript
+const uploadMethod = alovaInstance.Post('/todo/uploadfile', formData);
+const offUploadEvent = uploadMethod.onUpload(event => {
+ console.log('File size:', event.total);
+ console.log('Uploaded:', event.loaded);
+});
+
+uploadMethod.then(res => {
+ // ...
+});
+
+// Unbind upload callback
+const handleOffEvent = () => {
+ offUploadEvent();
+};
+```
+
+```javascript
+const downloadMethod = alovaInstance.Get('/todo/downloadfile');
+const offDownloadEvent = downloadMethod.onDownload(event => {
+ console.log('File size:', event.total);
+ console.log('Downloaded:', event.loaded);
+});
+
+downloadMethod.then(res => {
+ // ...
+});
+
+// Unbind download callback
+const handleOffEvent = () => {
+ offDownloadEvent();
+};
+```
+
+:::warning Things to note when using the `GlobalFetch` adapter
+
+Due to fetch api limitations, the **GlobalFetch** adapter provided by alova does not support upload progress. If you need to upload progress, please use [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr) or [axios Adapter](/tutorial/request-adapter/alova-adapter-axios).
+
+You can also write your own request adapter, see [Writing Request Adapter](/tutorial/custom/custom-http-adapter) for details.
+
+:::
+
+**Upload/Download Status Type**
+
+```typescript
+type Progress = {
+ /** Total amount of data uploaded or downloaded */
+ total: number;
+ /** Completed data */
+ loaded: number;
+};
+```
diff --git a/docs/tutorial/02-getting-started/04-alova.md b/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md
similarity index 95%
rename from docs/tutorial/02-getting-started/04-alova.md
rename to versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md
index 8a6456f6d..36a515094 100644
--- a/docs/tutorial/02-getting-started/04-alova.md
+++ b/versioned_docs/version-2.x/tutorial/02-getting-started/04-alova.md
@@ -1,67 +1,66 @@
----
-title: Alova Instance
-sidebar_position: 40
----
-
-Alova instances can not only create method instances of different types, but also set global parameters. The created method instances will inherit the parameters of this alova instance. When the parameters of the alova instance are set to the same parameters as the method instance, such as `timeout`, `shareRequest`, etc., the parameters of the method instance will be used first.
-
-Next we look at the global parameters of alova.
-
-## baseURL
-
-After setting the baseURL, you no longer need to add the same url prefix for every request.
-
-```javascript
-const alovaInstance = createAlova({
- baseURL: 'https://api.alovajs.dev'
- // ...
-});
-```
-
-At this point, you only need to specify pathname when creating a method instance.
-
-```javascript
-alovaInstance.Get('/todo/list');
-```
-
-## Global timeout
-
-The following is to set the global request timeout.
-
-```javascript
-// Set request timeout globally
-const alovaInstance = createAlova({
- // ...
- // highlight-start
- // Request timeout, unit is milliseconds, default is 0, which means it will never timeout
- timeout: 50000
- // highlight-end
-});
-```
-
-## Global sharing request
-
-Set sharing requests globally.
-
-```javascript
-const alovaInstance = createAlova({
- // ...
- // highlight-start
- shareRequest: false
- // highlight-end
-});
-```
-
-## Request adapter
-
-In the previous chapter we have configured the `GlobalFetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments.
-
-- [Mock request adapter](/tutorial/request-adapter/alova-mock)
-- [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr)
-- [axios adapter](/tutorial/request-adapter/alova-adapter-axios)
-- [uniapp adapter](/tutorial/request-adapter/alova-adapter-uniapp)
-- [taro adapter](/tutorial/request-adapter/alova-adapter-taro)
-
-## Global response cache
-
-You can also set the response cache globally, which we will explain in detail in the [Response Cache](/tutorial/cache/mode) chapter later.
+---
+title: Alova Instance
+---
+
+Alova instances can not only create method instances of different types, but also set global parameters. The created method instances will inherit the parameters of this alova instance. When the parameters of the alova instance are set to the same parameters as the method instance, such as `timeout`, `shareRequest`, etc., the parameters of the method instance will be used first.
+
+Next we look at the global parameters of alova.
+
+## baseURL
+
+After setting the baseURL, you no longer need to add the same url prefix for every request.
+
+```javascript
+const alovaInstance = createAlova({
+ baseURL: 'https://api.alovajs.dev'
+ // ...
+});
+```
+
+At this point, you only need to specify pathname when creating a method instance.
+
+```javascript
+alovaInstance.Get('/todo/list');
+```
+
+## Global timeout
+
+The following is to set the global request timeout.
+
+```javascript
+// Set request timeout globally
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ // Request timeout, unit is milliseconds, default is 0, which means it will never timeout
+ timeout: 50000
+ // highlight-end
+});
+```
+
+## Global sharing request
+
+Set sharing requests globally.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ shareRequest: false
+ // highlight-end
+});
+```
+
+## Request adapter
+
+In the previous chapter we have configured the `GlobalFetch` request adapter, which will be used to send requests initiated by this alova instance. In fact, we also provide various request adapters for different JS environments.
+
+- [Mock request adapter](/tutorial/request-adapter/alova-mock)
+- [XMLHttpRequest Adapter](/tutorial/request-adapter/alova-adapter-xhr)
+- [axios adapter](/tutorial/request-adapter/alova-adapter-axios)
+- [uniapp adapter](/tutorial/request-adapter/alova-adapter-uniapp)
+- [taro adapter](/tutorial/request-adapter/alova-adapter-taro)
+
+## Global response cache
+
+You can also set the response cache globally, which we will explain in detail in the [Response Cache](/tutorial/cache/mode) chapter later.
diff --git a/docs/tutorial/02-getting-started/05-global-interceptor.md b/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md
similarity index 97%
rename from docs/tutorial/02-getting-started/05-global-interceptor.md
rename to versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md
index fbb8d9e6b..b67bbceeb 100644
--- a/docs/tutorial/02-getting-started/05-global-interceptor.md
+++ b/versioned_docs/version-2.x/tutorial/02-getting-started/05-global-interceptor.md
@@ -1,137 +1,136 @@
----
-title: Global Interceptor
-sidebar_position: 50
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-## Global request interceptor
-
-Usually, we need to use the same configuration for all requests, such as adding token and timestamp to the request header. At this time, we can set a global request interceptor, which will be triggered before all requests. We can set this interceptor Set request parameters uniformly.
-
-```mermaid
-flowchart LR
- R1[Request 1] --> beforeRequest
- R2[Request 2] --> beforeRequest
- R3[Request 3] --> beforeRequest
- RN[Request N] --> beforeRequest
- beforeRequest --> S1[Send request]
-```
-
-```javascript
-const alovaInstance = createAlova({
- // ...
- //The function parameter is a method instance, including request data such as url, params, data, headers, etc.
- // You can modify these data freely
- // highlight-start
- beforeRequest(method) {
- // Suppose we need to add token to the request header
- method.config.headers.token = 'token';
- }
- // highlight-end
-});
-```
-
-You can also set beforeRequest as an asynchronous function.
-
-```javascript
-const alovaInstance = createAlova({
- // ...
- // highlight-start
- async beforeRequest(method) {
- //Perform some asynchronous tasks
- // ...
- }
- // highlight-end
-});
-```
-
-## Global response interceptor
-
-When we want to uniformly parse response data, uniformly handle errors, and uniformly handle request completion, we can specify a global response interceptor when creating an alova instance. The response interceptor includes an interceptor for successful requests and an interceptor for failed requests. and request completion interceptors.
-
-```mermaid
-flowchart LR
- classDef error fill:#f96,stroke:#f00,stroke-width:2px;
-
- R1[Request 1 successful] --> responded.onSuccess
- R2[Request 2 successful] --> responded.onSuccess
- RN[Request N successful] --> responded.onSuccess
- R4[Request 4 failed]:::error --> responded.onError:::error
- R5[Request M failed]:::error --> responded.onError:::error
- responded.onSuccess --> responded.onComplete
- responded.onError --> responded.onComplete
-```
-
-```javascript
-const alovaInstance = createAlova({
- // ...
- //Use two items of the array to specify the interceptor for successful request and the interceptor for failed request respectively.
- responded: {
- // highlight-start
- // Interceptor for successful request
- // When using the GlobalFetch request adapter, the first parameter receives the Response object
- // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request.
- onSuccess: async (response, method) => {
- if (response.status >= 400) {
- throw new Error(response.statusText);
- }
- const json = await response.json();
- if (json.code !== 200) {
- // This request will throw an error when an error is thrown or a Promise instance with reject status is returned.
- throw new Error(json.message);
- }
-
- //The parsed response data will be passed to the transformData hook function of the method instance. These functions will be explained later.
- return json.data;
- },
- // highlight-end
-
- // highlight-start
- // Interceptor for request failure
- // This interceptor will be entered when a request error occurs.
- // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request.
- onError: (err, method) => {
- alert(error.message);
- },
- // highlight-end
-
- // highlight-start
- //Interceptor for request completion
- // When you need to execute logic whether the request succeeds, fails, or hits the cache, you can specify a global `onComplete` interceptor when creating an alova instance, such as turning off the request loading state.
- // Receive the method instance of the current request
- onComplete: async method => {
- // Process request completion logic
- }
- // highlight-end
- }
-});
-```
-
-If you do not need to set an interceptor for request failure or completion, you can directly pass in the interceptor function for successful request, instead of setting a callback through an object.
-
-```javascript
-const alovaInstance = createAlova({
- // ...
- // highlight-start
- async responded(response, method) {
- // Interceptor for successful request
- }
- // highlight-end
-});
-```
-
-:::info Interceptor triggering instructions
-
-When you use `GlobalFetch` to request the adapter, due to the characteristics of `window.fetch`, the `onError` interceptor will only be triggered when the connection times out or the connection is aborted. In other cases, the `onSuccess` interceptor will be triggered. [For details, please Check here](https://developer.mozilla.org/docs/Web/API/fetch)
-
-:::
-
-:::warning Special attention
-
-1. `onSuccess`, `onError` and `onComplete` can be set as synchronous functions and asynchronous functions.
-2. The `onError` callback is a capture function for request errors. An error thrown in `onSuccess` will not trigger `onError`. When an error is caught but no error is thrown or a Promise instance that returns reject status is used, the request will be considered successful and no response data will be obtained.
-3. In 2.0.x and previous versions, `responded` was incorrectly spelled as `responsed`. In 2.1.0, the two have been made compatible. It is recommended to use `responded` instead of `responsed` in subsequent versions. .
-
-:::
+---
+title: Global Interceptor
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Global request interceptor
+
+Usually, we need to use the same configuration for all requests, such as adding token and timestamp to the request header. At this time, we can set a global request interceptor, which will be triggered before all requests. We can set this interceptor Set request parameters uniformly.
+
+```mermaid
+flowchart LR
+ R1[Request 1] --> beforeRequest
+ R2[Request 2] --> beforeRequest
+ R3[Request 3] --> beforeRequest
+ RN[Request N] --> beforeRequest
+ beforeRequest --> S1[Send request]
+```
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ //The function parameter is a method instance, including request data such as url, params, data, headers, etc.
+ // You can modify these data freely
+ // highlight-start
+ beforeRequest(method) {
+ // Suppose we need to add token to the request header
+ method.config.headers.token = 'token';
+ }
+ // highlight-end
+});
+```
+
+You can also set beforeRequest as an asynchronous function.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ async beforeRequest(method) {
+ //Perform some asynchronous tasks
+ // ...
+ }
+ // highlight-end
+});
+```
+
+## Global response interceptor
+
+When we want to uniformly parse response data, uniformly handle errors, and uniformly handle request completion, we can specify a global response interceptor when creating an alova instance. The response interceptor includes an interceptor for successful requests and an interceptor for failed requests. and request completion interceptors.
+
+```mermaid
+flowchart LR
+ classDef error fill:#f96,stroke:#f00,stroke-width:2px;
+
+ R1[Request 1 successful] --> responded.onSuccess
+ R2[Request 2 successful] --> responded.onSuccess
+ RN[Request N successful] --> responded.onSuccess
+ R4[Request 4 failed]:::error --> responded.onError:::error
+ R5[Request M failed]:::error --> responded.onError:::error
+ responded.onSuccess --> responded.onComplete
+ responded.onError --> responded.onComplete
+```
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ //Use two items of the array to specify the interceptor for successful request and the interceptor for failed request respectively.
+ responded: {
+ // highlight-start
+ // Interceptor for successful request
+ // When using the GlobalFetch request adapter, the first parameter receives the Response object
+ // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request.
+ onSuccess: async (response, method) => {
+ if (response.status >= 400) {
+ throw new Error(response.statusText);
+ }
+ const json = await response.json();
+ if (json.code !== 200) {
+ // This request will throw an error when an error is thrown or a Promise instance with reject status is returned.
+ throw new Error(json.message);
+ }
+
+ //The parsed response data will be passed to the transformData hook function of the method instance. These functions will be explained later.
+ return json.data;
+ },
+ // highlight-end
+
+ // highlight-start
+ // Interceptor for request failure
+ // This interceptor will be entered when a request error occurs.
+ // The second parameter is the method instance of the current request. You can use it to synchronize the configuration information before and after the request.
+ onError: (err, method) => {
+ alert(error.message);
+ },
+ // highlight-end
+
+ // highlight-start
+ //Interceptor for request completion
+ // When you need to execute logic whether the request succeeds, fails, or hits the cache, you can specify a global `onComplete` interceptor when creating an alova instance, such as turning off the request loading state.
+ // Receive the method instance of the current request
+ onComplete: async method => {
+ // Process request completion logic
+ }
+ // highlight-end
+ }
+});
+```
+
+If you do not need to set an interceptor for request failure or completion, you can directly pass in the interceptor function for successful request, instead of setting a callback through an object.
+
+```javascript
+const alovaInstance = createAlova({
+ // ...
+ // highlight-start
+ async responded(response, method) {
+ // Interceptor for successful request
+ }
+ // highlight-end
+});
+```
+
+:::info Interceptor triggering instructions
+
+When you use `GlobalFetch` to request the adapter, due to the characteristics of `window.fetch`, the `onError` interceptor will only be triggered when the connection times out or the connection is aborted. In other cases, the `onSuccess` interceptor will be triggered. [For details, please Check here](https://developer.mozilla.org/docs/Web/API/fetch)
+
+:::
+
+:::warning Special attention
+
+1. `onSuccess`, `onError` and `onComplete` can be set as synchronous functions and asynchronous functions.
+2. The `onError` callback is a capture function for request errors. An error thrown in `onSuccess` will not trigger `onError`. When an error is caught but no error is thrown or a Promise instance that returns reject status is used, the request will be considered successful and no response data will be obtained.
+3. In 2.0.x and previous versions, `responded` was incorrectly spelled as `responsed`. In 2.1.0, the two have been made compatible. It is recommended to use `responded` instead of `responsed` in subsequent versions. .
+
+:::
diff --git a/docs/tutorial/02-getting-started/06-method-metadata.md b/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md
similarity index 96%
rename from docs/tutorial/02-getting-started/06-method-metadata.md
rename to versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md
index 995782ccb..a89f55a09 100644
--- a/docs/tutorial/02-getting-started/06-method-metadata.md
+++ b/versioned_docs/version-2.x/tutorial/02-getting-started/06-method-metadata.md
@@ -1,157 +1,156 @@
----
-title: Method Metadata
-sidebar_position: 60
----
-
-:::info version requirements
-
-v2.7.0+
-
-:::
-
-Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata.
-
-## Use metadata to identify identities
-
-### Use identity before request
-
-For example, most of the interfaces in your project need to be accompanied by `token` for each request, but there are still some interfaces that do not require verification. You may handle them uniformly in the global `beforeRequest` function.
-
-```javascript
-const nonvalidateRequiredApi = [
- '/api/url1',
- '/api/url2',
- '/api/url3'
- // ...
-];
-
-createAlova({
- beforeRequest(method) {
- if (!nonvalidateRequiredApi.includes(method.url)) {
- method.config.headers.token = '...';
- }
- }
-});
-```
-
-This will cause the following two problems:
-
-1. Information is not aggregated with method instances, making maintainability even worse;
-2. Coding is more troublesome;
-
-To solve these two problems, we will use metadata to identify a specific method instance when it is created.
-
-**Step 1: Define metadata when creating method instance**
-
-```javascript
-const loginAPI = (username, password) => {
- const methodInstance = alovaInst.Post('/login', {
- username,
- password
- });
- methodInstance.meta = {
- ignoreToken: true
- };
- return methodInstance;
-};
-```
-
-**[2.18.0+]** And you can also directly define metadata in config
-
-```javascript
-const loginAPI = (username, password) => {
- return alovaInst.Post(
- '/login',
- {
- username,
- password
- },
- {
- meta: {
- ignoreToken: true
- }
- }
- );
-};
-```
-
-**Step 2: Use metadata as the basis for judgment in `beforeRequest`**
-
-```javascript
-createAlova({
- // ...
- beforeRequest(method) {
- if (!method.meta?.ignoreToken) {
- method.config.headers.token = '...';
- }
- }
-});
-```
-
-### Use the identity after the response
-
-This method can also be used in global `responded`. For example, in most cases, the request api will return json data, but there may be a file download interface, which will return a binary data stream. In this case Below, you can use different metadata in `responded` to handle different responses separately.
-
-**Step one: When creating a method instance, you also need to assign a metadata**
-
-```javascript
-const downloadAPI = filePath => {
- const methodInstance = alovaInst.Post('/download_file', {
- filePath
- });
- methodInstance.meta = {
- isDownload: true
- };
- return methodInstance;
-};
-```
-
-**Step 2: Use metadata in `responded` as a basis for judgment**
-
-```javascript
-createAlova({
- // ...
- responded:
- onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json()
- onError: (error, method) => {
- //Metadata of method instances can also be accessed when responding to errors
- }
- }
-});
-```
-
-## Use metadata to pass information
-
-In some cases, if you want to add additional information to different method instances for use elsewhere, you can also use metadata to save it. Take uniformly generating different method instance IDs as an example.
-
-```javascript
-createAlova({
- beforeRequest(method) {
- if (!method.meta.generateId) {
- method.meta.uid = generateUUID();
- }
- },
-
- responded: {
- onSuccess(response, method) {
- // Access the meta data generated by the current method when the request is successful.
- const currentMethodUID = method.meta.uid;
- },
- onError(error, method) {
- //Access the meta data generated by the current method when the request fails.
- const currentMethodUID = method.meta.uid;
- }
- }
-});
-```
-
-## Tips for non-typescript projects
-
-In a non-typescript environment, you can use any attribute as an information carrier, not limited to the `meta` attribute.
-
-```javascript
-methodInstance.showResponseMsg = true;
-methodInstance.others = 'abc';
-```
-
-Only in the typescript environment, any attribute name will report that the attribute "$0" does not exist. ts(2339)`, so in the type we specify the `meta` attribute as the information carrier.
+---
+title: Method Metadata
+---
+
+:::info version requirements
+
+v2.7.0+
+
+:::
+
+Method instances run through the entire request life cycle of alova, and there will be a large number of different method instances in the project. Sometimes we need to add additional information to specific method instances to facilitate their identification or additional information transfer. Wait, at this point, we need to use method metadata.
+
+## Use metadata to identify identities
+
+### Use identity before request
+
+For example, most of the interfaces in your project need to be accompanied by `token` for each request, but there are still some interfaces that do not require verification. You may handle them uniformly in the global `beforeRequest` function.
+
+```javascript
+const nonvalidateRequiredApi = [
+ '/api/url1',
+ '/api/url2',
+ '/api/url3'
+ // ...
+];
+
+createAlova({
+ beforeRequest(method) {
+ if (!nonvalidateRequiredApi.includes(method.url)) {
+ method.config.headers.token = '...';
+ }
+ }
+});
+```
+
+This will cause the following two problems:
+
+1. Information is not aggregated with method instances, making maintainability even worse;
+2. Coding is more troublesome;
+
+To solve these two problems, we will use metadata to identify a specific method instance when it is created.
+
+**Step 1: Define metadata when creating method instance**
+
+```javascript
+const loginAPI = (username, password) => {
+ const methodInstance = alovaInst.Post('/login', {
+ username,
+ password
+ });
+ methodInstance.meta = {
+ ignoreToken: true
+ };
+ return methodInstance;
+};
+```
+
+**[2.18.0+]** And you can also directly define metadata in config
+
+```javascript
+const loginAPI = (username, password) => {
+ return alovaInst.Post(
+ '/login',
+ {
+ username,
+ password
+ },
+ {
+ meta: {
+ ignoreToken: true
+ }
+ }
+ );
+};
+```
+
+**Step 2: Use metadata as the basis for judgment in `beforeRequest`**
+
+```javascript
+createAlova({
+ // ...
+ beforeRequest(method) {
+ if (!method.meta?.ignoreToken) {
+ method.config.headers.token = '...';
+ }
+ }
+});
+```
+
+### Use the identity after the response
+
+This method can also be used in global `responded`. For example, in most cases, the request api will return json data, but there may be a file download interface, which will return a binary data stream. In this case Below, you can use different metadata in `responded` to handle different responses separately.
+
+**Step one: When creating a method instance, you also need to assign a metadata**
+
+```javascript
+const downloadAPI = filePath => {
+ const methodInstance = alovaInst.Post('/download_file', {
+ filePath
+ });
+ methodInstance.meta = {
+ isDownload: true
+ };
+ return methodInstance;
+};
+```
+
+**Step 2: Use metadata in `responded` as a basis for judgment**
+
+```javascript
+createAlova({
+ // ...
+ responded:
+ onSuccess: (response, method) => method.meta?.isDownload ? response.blob() : response.json()
+ onError: (error, method) => {
+ //Metadata of method instances can also be accessed when responding to errors
+ }
+ }
+});
+```
+
+## Use metadata to pass information
+
+In some cases, if you want to add additional information to different method instances for use elsewhere, you can also use metadata to save it. Take uniformly generating different method instance IDs as an example.
+
+```javascript
+createAlova({
+ beforeRequest(method) {
+ if (!method.meta.generateId) {
+ method.meta.uid = generateUUID();
+ }
+ },
+
+ responded: {
+ onSuccess(response, method) {
+ // Access the meta data generated by the current method when the request is successful.
+ const currentMethodUID = method.meta.uid;
+ },
+ onError(error, method) {
+ //Access the meta data generated by the current method when the request fails.
+ const currentMethodUID = method.meta.uid;
+ }
+ }
+});
+```
+
+## Tips for non-typescript projects
+
+In a non-typescript environment, you can use any attribute as an information carrier, not limited to the `meta` attribute.
+
+```javascript
+methodInstance.showResponseMsg = true;
+methodInstance.others = 'abc';
+```
+
+Only in the typescript environment, any attribute name will report that the attribute "$0" does not exist. ts(2339)`, so in the type we specify the `meta` attribute as the information carrier.
diff --git a/docs/tutorial/02-getting-started/README.md b/versioned_docs/version-2.x/tutorial/02-getting-started/README.md
similarity index 98%
rename from docs/tutorial/02-getting-started/README.md
rename to versioned_docs/version-2.x/tutorial/02-getting-started/README.md
index 6d845e06c..a5c34b190 100644
--- a/docs/tutorial/02-getting-started/README.md
+++ b/versioned_docs/version-2.x/tutorial/02-getting-started/README.md
@@ -1,136 +1,136 @@
----
-title: What is alova
----
-
-import Link from '@docusaurus/Link';
-import NavCard from '@site/src/components/NavCard';
-import SupportList from '@site/src/components/SupportList';
-
-alova is a lightweight request strategy library. It provides a complete set of solutions to deal with complex request scenarios. We call it **Request Strategy**. It only takes one line of code to quickly implement various complex request logics. , not only can help you improve development efficiency, but also help you improve the operating efficiency of the App and reduce the pressure on the server.
-
-Here is the simplest request example:
-
-```javascript
-const response = await alova.Get('/api/user');
-```
-
-Ordinary? Let's look at another example of automatically managing request status. **loading, error, and data are responsive data**. In UI frameworks such as react, vue, svelte, etc., they can be bound directly in the view and will be processed according to the request. State automatically maintains this responsive data.
-
-```javascript
-const { loading, error, data } = useRequest(alova.Get('/api/user'));
-```
-
-The following is an example of a paging request strategy that automatically triggers requests with different parameters when page, pageSize, etc. change.
-
-```javascript
-const { loading, error, data, page, pageSize, total } = usePagination((page, size) =>
- alova.Get('/api/user/list', {
- params: { page, size }
- })
-);
-```
-
-alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook.
-
-## Core useHook
-
-useRequest
-useWatcher
-useFetcher
-
-## Scenario-based request strategy
-
-usePagination
-useSQRequest
-useForm
-TokenAuthentication
-useAutoRequest
-useCaptcha
-actionDelegationMiddleware
-useSerialRequest
-useSerialWatcher
-useRetriableRequest
-useUploader
-useSSE
-
-## High flexibility
-
-Thanks to the high flexibility of alova, you can use it with different request libraries in the following different JS environments (the gray part will be gradually supported in the future).
-
-
-
-## Is there any difference?
-
-Unlike other request libraries, alova's goal is to make requests simpler and maintain more efficient data interaction.
-
-We consider both developers and App users. For developers, alova provides them with a simple request API and an out-of-the-box high-performance request strategy module. For application users, they can enjoy Alova's high-performance data interaction brings a smooth experience.
-
-In addition, let’s take a look at the specific features:
-
-- API design similar to axios, allowing users to learn at a lower cost;
-- 10+ out-of-the-box high-performance request strategies to make applications smoother;
-- alova is lightweight, only 4kb+, which is 30%+ of axios;
-- High flexibility, alova's adapter allows alova to be used in any js environment and with any UI framework (the built-in supported UI framework is `vue/react/svelte`), and provides a unified experience and perfect code migrate;
-- 3 caching modes and request sharing mechanism to improve request performance and reduce server pressure;
-- Highly aggregated organization of API code. The request parameters, cache behavior, response data conversion, etc. of each API will be gathered in the same code block, which has great advantages for managing a large number of APIs;
-
-In [alova's future](/tutorial/others/future), further request simplification will be implemented.
-
-:::info compared to other request libraries
-
-You can also check out [Comparison with other request libraries](/tutorial/others/comparison) to learn more about how alova is different.
-
-:::
-
-## Online trial
-
-You can run the project directly in the browser via Codesandbox [online editable examples try alovajs](/category/examples), so it's almost indistinguishable from local development without having to install anything on your machine.
-
-## Join alova community
-
-import ImgDiscord from '@site/static/img/discord.svg';
-import ImgX from '@site/static/img/x.svg';
-import ImgWechat from '@site/static/img/wechat.svg';
-import wechatQrcode from '@site/static/img/wechat_qrcode.jpg';
-
-,
-title: 'Discord',
-desc: 'The community\'s GPT robot will answer your questions',
-link: 'https://discord.gg/S47QGJgkVb',
-target: '__blank'
-},
-{
-Image: ,
-title: 'WeChat',
-desc: 'Communicate in group chat and get responses faster',
-link: wechatQrcode,
-target: '__blank'
-},
-{
-Image: ,
-title: 'X',
-desc: 'Follow us and continue to receive the latest news',
-link: 'https://x.com/alovajs',
-target: '__blank'
-}
-]}>
-
-## Welcome to contribute
-
-Before contributing, please be sure to read the [Contribution Guide](/contributing/overview) in detail to ensure your effective contribution.
-
-## Start
-
-Next, we will start with the simplest request, then explain the request strategy, understand how alova simplifies your work, and then go into the advanced guide and the best practices summarized in actual projects.
-
-Let’s start learning to send our first request!
-
-
+---
+title: What is alova
+---
+
+import Link from '@docusaurus/Link';
+import NavCard from '@site/src/components/NavCard';
+import SupportList from '@site/src/components/SupportList';
+
+alova is a lightweight request strategy library. It provides a complete set of solutions to deal with complex request scenarios. We call it **Request Strategy**. It only takes one line of code to quickly implement various complex request logics. , not only can help you improve development efficiency, but also help you improve the operating efficiency of the App and reduce the pressure on the server.
+
+Here is the simplest request example:
+
+```javascript
+const response = await alova.Get('/api/user');
+```
+
+Ordinary? Let's look at another example of automatically managing request status. **loading, error, and data are responsive data**. In UI frameworks such as react, vue, svelte, etc., they can be bound directly in the view and will be processed according to the request. State automatically maintains this responsive data.
+
+```javascript
+const { loading, error, data } = useRequest(alova.Get('/api/user'));
+```
+
+The following is an example of a paging request strategy that automatically triggers requests with different parameters when page, pageSize, etc. change.
+
+```javascript
+const { loading, error, data, page, pageSize, total } = usePagination((page, size) =>
+ alova.Get('/api/user/list', {
+ params: { page, size }
+ })
+);
+```
+
+alova provides 10+ request strategy modules based on the [RSM](/tutorial/others/RSM) specification, which are implemented in the form of useHook.
+
+## Core useHook
+
+useRequest
+useWatcher
+useFetcher
+
+## Scenario-based request strategy
+
+usePagination
+useSQRequest
+useForm
+TokenAuthentication
+useAutoRequest
+useCaptcha
+actionDelegationMiddleware
+useSerialRequest
+useSerialWatcher
+useRetriableRequest
+useUploader
+useSSE
+
+## High flexibility
+
+Thanks to the high flexibility of alova, you can use it with different request libraries in the following different JS environments (the gray part will be gradually supported in the future).
+
+
+
+## Is there any difference?
+
+Unlike other request libraries, alova's goal is to make requests simpler and maintain more efficient data interaction.
+
+We consider both developers and App users. For developers, alova provides them with a simple request API and an out-of-the-box high-performance request strategy module. For application users, they can enjoy Alova's high-performance data interaction brings a smooth experience.
+
+In addition, let’s take a look at the specific features:
+
+- API design similar to axios, allowing users to learn at a lower cost;
+- 10+ out-of-the-box high-performance request strategies to make applications smoother;
+- alova is lightweight, only 4kb+, which is 30%+ of axios;
+- High flexibility, alova's adapter allows alova to be used in any js environment and with any UI framework (the built-in supported UI framework is `vue/react/svelte`), and provides a unified experience and perfect code migrate;
+- 3 caching modes and request sharing mechanism to improve request performance and reduce server pressure;
+- Highly aggregated organization of API code. The request parameters, cache behavior, response data conversion, etc. of each API will be gathered in the same code block, which has great advantages for managing a large number of APIs;
+
+In [alova's future](/tutorial/others/future), further request simplification will be implemented.
+
+:::info compared to other request libraries
+
+You can also check out [Comparison with other request libraries](/tutorial/others/comparison) to learn more about how alova is different.
+
+:::
+
+## Online trial
+
+You can run the project directly in the browser via Codesandbox [online editable examples try alovajs](/category/examples), so it's almost indistinguishable from local development without having to install anything on your machine.
+
+## Join alova community
+
+import ImgDiscord from '@site/static/img/discord.svg';
+import ImgX from '@site/static/img/x.svg';
+import ImgWechat from '@site/static/img/wechat.svg';
+import wechatQrcode from '@site/static/img/wechat_qrcode.jpg';
+
+,
+title: 'Discord',
+desc: 'The community\'s GPT robot will answer your questions',
+link: 'https://discord.gg/S47QGJgkVb',
+target: '__blank'
+},
+{
+Image: ,
+title: 'WeChat',
+desc: 'Communicate in group chat and get responses faster',
+link: wechatQrcode,
+target: '__blank'
+},
+{
+Image: ,
+title: 'X',
+desc: 'Follow us and continue to receive the latest news',
+link: 'https://x.com/alovajs',
+target: '__blank'
+}
+]}>
+
+## Welcome to contribute
+
+Before contributing, please be sure to read the [Contribution Guide](/contributing/overview) in detail to ensure your effective contribution.
+
+## Start
+
+Next, we will start with the simplest request, then explain the request strategy, understand how alova simplifies your work, and then go into the advanced guide and the best practices summarized in actual projects.
+
+Let’s start learning to send our first request!
+
+
diff --git a/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json b/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json
new file mode 100644
index 000000000..41f4c00e7
--- /dev/null
+++ b/versioned_docs/version-2.x/tutorial/02-getting-started/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Getting Started"
+}
diff --git a/docs/tutorial/03-combine-framework/02-use-request.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md
similarity index 99%
rename from docs/tutorial/03-combine-framework/02-use-request.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md
index 0cba915dc..af02e9b55 100644
--- a/docs/tutorial/03-combine-framework/02-use-request.md
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/02-use-request.md
@@ -1,6 +1,5 @@
---
title: Auto Manage States
-sidebar_position: 20
---
import Tabs from '@theme/Tabs';
diff --git a/docs/tutorial/03-combine-framework/03-use-watcher.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md
similarity index 88%
rename from docs/tutorial/03-combine-framework/03-use-watcher.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md
index ce8fca0f7..5d8843d41 100644
--- a/docs/tutorial/03-combine-framework/03-use-watcher.md
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/03-use-watcher.md
@@ -1,315 +1,322 @@
----
-title: Watching Request
-sidebar_position: 30
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import EmbedSandpack from "@site/src/components/EmbedSandpack";
-import CodeBlock from '@theme/CodeBlock';
-import useWatcherSearchVue from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueComposition-search.en.vue';
-import useWatcherSearchReact from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/react-search.en.jsx';
-import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/svelte-search.en.svelte';
-import useWatcherSearchVueOptions from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueOptions-search.en.vue';
-
-In some scenarios that require re-requesting as data changes, such as paging, data filtering, and fuzzy search, you can use `useWatcher` to monitor the specified states change and send a request immediately.
-
-## Keyword search
-
-Next we take searching for todo items as an example. Try to change options in the search box and see how the todo list changes.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{useWatcherSearchSvelte}
-
-
-
-
-
-
-
-
-
-## Pagination
-
-Taking the todo list pagination request as an example, you can do this.
-
-
-
-
-```html
-
-
-
-
-
-```
-
-
-
-
-```jsx
-import { useState } from 'react';
-
-//method instance creation function
-const getTodoList = currentPage => {
- return alovaInstance.Get('/todo/list', {
- params: {
- currentPage,
- pageSize: 10
- }
- });
-};
-
-const App = () => {
- const [currentPage, setCurrentPage] = useState(1);
- const {
- loading,
- data,
- error
-
- // The first parameter is the function that returns the method instance, not the method instance itself
- } = useWatcher(
- () => getTodoList(currentPage),
- // The watched states array, these states changes will trigger a request
- [currentPage],
- {
- // ⚠️Calling useWatcher is not triggered by default. Please note the difference from useRequest.
- // Manually set immediate to true to initially obtain the data on page 1
- immediate: true
- }
- );
-
- return {
- /* ... */
- };
-};
-```
-
-
-
-
-```html
-
-
-
-```
-
-
-
-
-```html
-
-
-
-
-
-```
-
-
-
-
-## Send request immediately
-
-`useWatcher` can also send the request immediately, but it should be noted that the `immediate` attribute of `useWatcher` defaults to `false`.
-
-```javascript
-const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], {
- // highlight-start
- immediate: true
- // highlight-end
-});
-send();
-```
-
-## Request debounce
-
-Usually we write debounce code at the level of frequently triggered events. This time we implemented the debounce function at the request level, which means you no longer have to implement debounce yourself in the fuzzy search function, and the usage is also very simple.
-
-:::info Tips: What is debounce?
-
-Debounce means that after an event is triggered, the function can only be executed once within n seconds. If an event is triggered again within n seconds after the event is triggered, the function delay execution time will be recalculated (here to distinguish between throttling, throttling means that the event cannot be triggered again within a period of time after the event is triggered)
-
-:::
-
-### Set debounce time of all watching states
-
-```javascript
-const { loading, data, error } = useWatcher(() => filterTodoList(keyword, date), [keyword, date], {
- // highlight-start
- // When debounce is set to a number, it represents the anti-bounce time of all watching states, in milliseconds.
- // This means that when one or more of the states keyword and date change, the request will be sent after 500ms.beg
- debounce: 500
- // highlight-end
-});
-```
-
-### Set debounce time for a single watching state
-
-In many scenarios, we only need to debounce certain frequently changing watching states, such as state changes triggered by `onInput` of a text box. You can do this:
-
-```javascript
-const { loading, data, error } = useWatcher(() => filterTodoList(keyword, date), [keyword, date], {
- // highlight-start
- // Set debounce time in the order of the array of watching states. 0 or not passed means no debounce.
- // The order of the watching states here is [keyword, date], and the debounce array setting is [500, 0], which means that only the debounce is set separately for the keyword.
- Debounce: [500, 0]
- // You can also set it as follows:
- // debounce: [500],
- // highlight-end
-});
-```
-
-## Block request when the states changes
-
-Sometimes you want not to send a request when the watched state changes. You can use the sendable attribute in the Hook configuration to control whether to send a request when the watched state changes. The sendable attribute is a function whose parameter is the `AlovaEvent` event object. Contains the array `sendArgs` composed of the parameters passed in by the `send` function, and the `method` instance of the current request, and the function returns a `truthy/falsy` value to determine whether the request needs to be triggered when the states changes (default is `true`), **throwing an error also means not triggering the request**.
-
-```javascript
-useWatcher(
- () => getTodoList($currentPage),
- // The watched states array, these states changes will trigger a request
- [state],
- {
- // highlight-start
- sendable: ({ sendArgs, method }) => {
- // do something
- // Only send request when state is 1
- return state === 1;
- }
- // highlight-end
- }
-);
-```
-
-## Request timing
-
-Sometimes when the states watched by `useWatcher` changes continuously resulting in the initiation of consecutive requests, the latter request gets a response before the previous request, but when the previous request gets a response, it will overwrite the response of the latter request. Resulting in getting a response that does not match the state; for example, if the state `state` changes, a request `1` is issued, and then when the request `1` has not responded, the value of `state` is changed and a request` is issued. 2`, if request `1` is returned after request `2`, the final response data will remain at request `1`.
-So we designed the `abortLast` parameter, which is used to mark whether to abort the last unresponsive request when the next request is issued. The default is `true`, so that the request issued by `useWatcher` is only valid for the last time.
-
-```mermaid
-sequenceDiagram
- participant U as User
- participant S as Server
- U ->> U: watch state
- U ->> S: state change to trigger request 1
- U ->> S: state change to trigger request 2
- S ->> U: Request 2 responded first
- S ->> U: Then request 1 responded
- U ->> U: Response of request 2 is overwritten
-```
-
-```javascript
-useWatcher(
- () => getTodoList($currentPage),
- // The watched states array, these states changes will trigger a request
- [state],
- {
- // highlight-start
- abortLast: true // Whether to abort the last unresponsive request, the default is true
- // highlight-end
- }
-);
-```
-
-:::warning Notes
-
-`abortLast` defaults to `true`. If it is changed to `false`, it may cause the problem of states and response mismatch.
-
-:::
+---
+title: Watching Request
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import EmbedSandpack from "@site/src/components/EmbedSandpack";
+import CodeBlock from '@theme/CodeBlock';
+import useWatcherSearchVue from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueComposition-search.en.vue';
+import useWatcherSearchReact from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/react-search.en.jsx';
+import useWatcherSearchSvelte from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/svelte-search.en.svelte';
+import useWatcherSearchVueOptions from '!!raw-loader!@site/codesandbox/03-learning/04-use-watcher/vueOptions-search.en.vue';
+
+In some scenarios that require re-requesting as data changes, such as paging, data filtering, and fuzzy search, you can use `useWatcher` to monitor the specified states change and send a request immediately.
+
+## Keyword search
+
+Next we take searching for todo items as an example. Try to change options in the search box and see how the todo list changes.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{useWatcherSearchSvelte}
+
+
+
+
+
+
+
+
+
+## Pagination
+
+Taking the todo list pagination request as an example, you can do this.
+
+
+
+
+```html
+
+
+
+
+
+```
+
+
+
+
+```jsx
+import { useState } from 'react';
+
+//method instance creation function
+const getTodoList = currentPage => {
+ return alovaInstance.Get('/todo/list', {
+ params: {
+ currentPage,
+ pageSize: 10
+ }
+ });
+};
+
+const App = () => {
+ const [currentPage, setCurrentPage] = useState(1);
+ const {
+ loading,
+ data,
+ error
+
+ // The first parameter is the function that returns the method instance, not the method instance itself
+ } = useWatcher(
+ () => getTodoList(currentPage),
+ // The watched states array, these states changes will trigger a request
+ [currentPage],
+ {
+ // ⚠️Calling useWatcher is not triggered by default. Please note the difference from useRequest.
+ // Manually set immediate to true to initially obtain the data on page 1
+ immediate: true
+ }
+ );
+
+ return {
+ /* ... */
+ };
+};
+```
+
+
+
+
+```html
+
+
+
+```
+
+
+
+
+```html
+
+
+
+
+
+```
+
+
+
+
+## Send request immediately
+
+`useWatcher` can also send the request immediately, but it should be noted that the `immediate` attribute of `useWatcher` defaults to `false`.
+
+```javascript
+const { send } = useWatcher(() => getTodoList(currentPage), [currentPage], {
+ // highlight-start
+ immediate: true
+ // highlight-end
+});
+send();
+```
+
+## Request debounce
+
+Usually we write debounce code at the level of frequently triggered events. This time we implemented the debounce function at the request level, which means you no longer have to implement debounce yourself in the fuzzy search function, and the usage is also very simple.
+
+:::info Tips: What is debounce?
+
+Debounce means that after an event is triggered, the function can only be executed once within n seconds. If an event is triggered again within n seconds after the event is triggered, the function delay execution time will be recalculated (here to distinguish between throttling, throttling means that the event cannot be triggered again within a period of time after the event is triggered)
+
+:::
+
+### Set debounce time of all watching states
+
+```javascript
+const { loading, data, error } = useWatcher(
+ () => filterTodoList(keyword, date),
+ [keyword, date],
+ {
+ // highlight-start
+ // When debounce is set to a number, it represents the anti-bounce time of all watching states, in milliseconds.
+ // This means that when one or more of the states keyword and date change, the request will be sent after 500ms.beg
+ debounce: 500
+ // highlight-end
+ }
+);
+```
+
+### Set debounce time for a single watching state
+
+In many scenarios, we only need to debounce certain frequently changing watching states, such as state changes triggered by `onInput` of a text box. You can do this:
+
+```javascript
+const { loading, data, error } = useWatcher(
+ () => filterTodoList(keyword, date),
+ [keyword, date],
+ {
+ // highlight-start
+ // Set debounce time in the order of the array of watching states. 0 or not passed means no debounce.
+ // The order of the watching states here is [keyword, date], and the debounce array setting is [500, 0], which means that only the debounce is set separately for the keyword.
+ Debounce: [500, 0]
+ // You can also set it as follows:
+ // debounce: [500],
+ // highlight-end
+ }
+);
+```
+
+## Block request when the states changes
+
+Sometimes you want not to send a request when the watched state changes. You can use the sendable attribute in the Hook configuration to control whether to send a request when the watched state changes. The sendable attribute is a function whose parameter is the `AlovaEvent` event object. Contains the array `sendArgs` composed of the parameters passed in by the `send` function, and the `method` instance of the current request, and the function returns a `truthy/falsy` value to determine whether the request needs to be triggered when the states changes (default is `true`), **throwing an error also means not triggering the request**.
+
+```javascript
+useWatcher(
+ () => getTodoList($currentPage),
+ // The watched states array, these states changes will trigger a request
+ [state],
+ {
+ // highlight-start
+ sendable: ({ sendArgs, method }) => {
+ // do something
+ // Only send request when state is 1
+ return state === 1;
+ }
+ // highlight-end
+ }
+);
+```
+
+## Request timing
+
+Sometimes when the states watched by `useWatcher` changes continuously resulting in the initiation of consecutive requests, the latter request gets a response before the previous request, but when the previous request gets a response, it will overwrite the response of the latter request. Resulting in getting a response that does not match the state; for example, if the state `state` changes, a request `1` is issued, and then when the request `1` has not responded, the value of `state` is changed and a request` is issued. 2`, if request `1` is returned after request `2`, the final response data will remain at request `1`.
+So we designed the `abortLast` parameter, which is used to mark whether to abort the last unresponsive request when the next request is issued. The default is `true`, so that the request issued by `useWatcher` is only valid for the last time.
+
+```mermaid
+sequenceDiagram
+ participant U as User
+ participant S as Server
+ U ->> U: watch state
+ U ->> S: state change to trigger request 1
+ U ->> S: state change to trigger request 2
+ S ->> U: Request 2 responded first
+ S ->> U: Then request 1 responded
+ U ->> U: Response of request 2 is overwritten
+```
+
+```javascript
+useWatcher(
+ () => getTodoList($currentPage),
+ // The watched states array, these states changes will trigger a request
+ [state],
+ {
+ // highlight-start
+ abortLast: true // Whether to abort the last unresponsive request, the default is true
+ // highlight-end
+ }
+);
+```
+
+:::warning Notes
+
+`abortLast` defaults to `true`. If it is changed to `false`, it may cause the problem of states and response mismatch.
+
+:::
diff --git a/docs/tutorial/03-combine-framework/04-initial-data.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md
similarity index 96%
rename from docs/tutorial/03-combine-framework/04-initial-data.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md
index 6a37d4136..2a7c5546e 100644
--- a/docs/tutorial/03-combine-framework/04-initial-data.md
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/04-initial-data.md
@@ -1,6 +1,5 @@
---
title: Initial Data
-sidebar_position: 40
---
When using `useRequest`, its `data` value defaults to undefined before the request is successful, but sometimes we need `data` to have an initial value before the request is successful. For example, when requesting a list, we usually need to initialize it to `[]`, otherwise it will throw error when rendering the list.
diff --git a/docs/tutorial/03-combine-framework/05-response.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md
similarity index 99%
rename from docs/tutorial/03-combine-framework/05-response.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md
index 231b07f59..b88cccf9a 100644
--- a/docs/tutorial/03-combine-framework/05-response.md
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/05-response.md
@@ -1,6 +1,5 @@
---
title: Process Response
-sidebar_position: 50
---
import Tabs from '@theme/Tabs';
diff --git a/docs/tutorial/03-combine-framework/06-abort-request.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md
similarity index 98%
rename from docs/tutorial/03-combine-framework/06-abort-request.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md
index 404e478af..d51fbc734 100644
--- a/docs/tutorial/03-combine-framework/06-abort-request.md
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/06-abort-request.md
@@ -1,6 +1,5 @@
---
title: Abort Request
-sidebar_position: 60
---
Receive `abort` for manual abort request via useHook.
diff --git a/docs/tutorial/03-combine-framework/07-download-upload-progress.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md
similarity index 95%
rename from docs/tutorial/03-combine-framework/07-download-upload-progress.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md
index 5ffba3d35..a91782a79 100644
--- a/docs/tutorial/03-combine-framework/07-download-upload-progress.md
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/07-download-upload-progress.md
@@ -1,207 +1,206 @@
----
-title: Download & Upload Progress
-sidebar_position: 70
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-useHook provides `downloading` and `uploading` for displaying progress information directly in the view.
-
-## Download progress
-
-For performance reasons, there is no progress information in `downloading` by default. You need to set `enableDownload` of the method instance to `true`, which will continuously update the `downloading` status during the download process.
-
-
-
-
-```html
-
-
-
-
-
-```
-
-
-
-
-## Upload progress
-
-Using the upload progress status is the same as using the download progress. First enable the upload progress information through `enableUpload`, and then receive it by receiving the `uploading` responsive state.
-
-
-
-
-```html
-
-
-
-
-
-```
-
-
-
+---
+title: Download & Upload Progress
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+useHook provides `downloading` and `uploading` for displaying progress information directly in the view.
+
+## Download progress
+
+For performance reasons, there is no progress information in `downloading` by default. You need to set `enableDownload` of the method instance to `true`, which will continuously update the `downloading` status during the download process.
+
+
+
+
+```html
+
+
+
+
+
+```
+
+
+
+
+## Upload progress
+
+Using the upload progress status is the same as using the download progress. First enable the upload progress information through `enableUpload`, and then receive it by receiving the `uploading` responsive state.
+
+
+
+
+```html
+
+
+
+
+
+```
+
+
+
diff --git a/docs/tutorial/03-combine-framework/08-receive-params.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md
similarity index 96%
rename from docs/tutorial/03-combine-framework/08-receive-params.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md
index fa4686bdb..a5f856185 100644
--- a/docs/tutorial/03-combine-framework/08-receive-params.md
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/08-receive-params.md
@@ -1,6 +1,5 @@
---
title: Receive Params
-sidebar_position: 80
---
In both `useRequest` and `useWatcher` we can call the `send` function to manually trigger the request. When the send function triggers the request, any number of parameters can be passed in. These parameters can actually be received by the following three locations.
@@ -32,7 +31,9 @@ send(2);
Received through `event.sendArgs` in the event callback function, which is an array containing all parameters of the send function.
```javascript
-const { send, onSuccess, onError, onComplete } = useRequest(newTodo => alovaInstance.Post('/todo', newTodo));
+const { send, onSuccess, onError, onComplete } = useRequest(newTodo =>
+ alovaInstance.Post('/todo', newTodo)
+);
onSuccess(event => {
//The value of sendArgs is [1]
console.log(event.sendArgs);
diff --git a/docs/tutorial/03-combine-framework/10-typescript.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md
similarity index 96%
rename from docs/tutorial/03-combine-framework/10-typescript.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md
index de279e29c..91e48217e 100644
--- a/docs/tutorial/03-combine-framework/10-typescript.md
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/10-typescript.md
@@ -1,159 +1,158 @@
----
-title: Typescript
-sidebar_position: 100
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-In terms of Typescript, we have indeed spent a lot of effort on optimization in order to provide a better user experience. We try our best to use automatic type inference to reduce the trouble of defining types for you.
-
-## Automatically infer alova useHooks states type
-
-When createAlova creates an alova instance, the state types created by `useRequest`, `useWatcher`, and `useFetcher` will be automatically inferred based on the passed `statesHook`. Currently only Vue, React, and Svelte are supported.
-
-> `useFetcher` is a useHook used for data fetching. For details, please read [Advanced-Data fetching chapter](/tutorial/advanced/use-fetcher).
-
-The following are the status types returned by useHooks by default.
-
-
-
-
-```typescript
-const vueAlova = createAlova({
- statesHook: VueHook
- // ...
-});
-const {
- loading, // Ref
- data, // Ref<{ data: any }>
- error // Ref
-} = useRequest(vueAlova.Get<{ data: any }>('/todo/list'));
-```
-
-
-
-
-```typescript
-const reactAlova = createAlova({
- statesHook: ReactHook
- // ...
-});
-const {
- loading, // boolean
- data, // { data: any }
- error // Error
-} = useRequest(reactAlova.Get<{ data: any }>('/todo/list'));
-```
-
-
-
-
-```typescript
-const svelteAlova = createAlova({
- statesHook: SvelteHook
- // ...
-});
-const {
- loading, // Writable
- data, // Writable<{ data: any }>
- error // Writable
-} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list'));
-```
-
-
-
-
-The type of data will be different depending on the response data type specified in different Method instances, let's continue to look below.
-
-## Type of response data
-
-When you specify a type for a data interface, you need to divide it into two situations.
-
-### Case 1
-
-When the response data does not need to be converted by calling `transformData`, the type can be specified directly through generics.
-
-```typescript
-interface Todo {
- title: string;
- time: string;
- done: boolean;
-}
-const Get = alovaInstance.Get('/todo/list');
-const { data } = useRequest(Get);
-// vue: The type of data is Ref
-// react: The type of data is Todo[]
-// svelte: The type of data is Writable
-```
-
-### Case 2
-
-When the response data needs to be converted by calling `transformData`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type.
-
-```typescript
-interface Todo {
- title: string;
- time: string;
- done: boolean;
-}
-const Get = alovaInstance.Get('/todo/list', {
- //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type.
- transformData(data: Todo[], headers) {
- return data.map(item => ({
- ...item,
- status: item.done ? 'Completed' : 'Not completed'
- }));
- }
-});
-
-const { data } = useRequest(Get);
-// vue: The type of data is Ref<(Todo & { status: string })[]>
-// react: The type of data is (Todo & { status: string })[]
-// svelte: The type of data is Writable<(Todo & { status: string })[]>
-```
-
-:::warning note
-
-The response data is converted by the global response interceptor, so when setting the type, it should also be set to the converted type.
-
-:::
-
-## Type inferred from request adapter
-
-Because alova supports custom request adapters, and the request configuration objects, response objects, and response headers of different adapters may be different, so the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created The types will be automatically inferred based on the types provided by the request adapter. Let's look at these types first.
-
-If you are using [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts), alova will automatically infer the type using `fetch api`, The types of fetch api are as follows.
-
-```typescript
-declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise;
-```
-
-### Method configuration type of instance
-
-The method configuration type will be automatically inferred as:
-
-```typescript
-// AlovaMethodCommonConfig is a unified request parameter and behavior parameter
-// highlight-start
-const methodConfig: AlovaMethodCommonConfig & RequestInit = {
- // highlight-end
- // ...
-};
-alovaInstance.Get('/api/user', methodConfig);
-```
-
-### Global response interceptor parameter type
-
-The type of responded interceptor will be automatically inferred as:
-
-```typescript
-createAlova({
- // ...
- // highlight-start
- responded: (response: Response, method: Method) => {
- // highlight-end
- // ...
- }
-});
-```
+---
+title: Typescript
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In terms of Typescript, we have indeed spent a lot of effort on optimization in order to provide a better user experience. We try our best to use automatic type inference to reduce the trouble of defining types for you.
+
+## Automatically infer alova useHooks states type
+
+When createAlova creates an alova instance, the state types created by `useRequest`, `useWatcher`, and `useFetcher` will be automatically inferred based on the passed `statesHook`. Currently only Vue, React, and Svelte are supported.
+
+> `useFetcher` is a useHook used for data fetching. For details, please read [Advanced-Data fetching chapter](/tutorial/advanced/use-fetcher).
+
+The following are the status types returned by useHooks by default.
+
+
+
+
+```typescript
+const vueAlova = createAlova({
+ statesHook: VueHook
+ // ...
+});
+const {
+ loading, // Ref
+ data, // Ref<{ data: any }>
+ error // Ref
+} = useRequest(vueAlova.Get<{ data: any }>('/todo/list'));
+```
+
+
+
+
+```typescript
+const reactAlova = createAlova({
+ statesHook: ReactHook
+ // ...
+});
+const {
+ loading, // boolean
+ data, // { data: any }
+ error // Error
+} = useRequest(reactAlova.Get<{ data: any }>('/todo/list'));
+```
+
+
+
+
+```typescript
+const svelteAlova = createAlova({
+ statesHook: SvelteHook
+ // ...
+});
+const {
+ loading, // Writable
+ data, // Writable<{ data: any }>
+ error // Writable
+} = useRequest(svelteAlova.Get<{ data: any }>('/todo/list'));
+```
+
+
+
+
+The type of data will be different depending on the response data type specified in different Method instances, let's continue to look below.
+
+## Type of response data
+
+When you specify a type for a data interface, you need to divide it into two situations.
+
+### Case 1
+
+When the response data does not need to be converted by calling `transformData`, the type can be specified directly through generics.
+
+```typescript
+interface Todo {
+ title: string;
+ time: string;
+ done: boolean;
+}
+const Get = alovaInstance.Get('/todo/list');
+const { data } = useRequest(Get);
+// vue: The type of data is Ref
+// react: The type of data is Todo[]
+// svelte: The type of data is Writable
+```
+
+### Case 2
+
+When the response data needs to be converted by calling `transformData`, the type needs to be specified in the conversion function parameter, and then its return value type will be used as the response data type.
+
+```typescript
+interface Todo {
+ title: string;
+ time: string;
+ done: boolean;
+}
+const Get = alovaInstance.Get('/todo/list', {
+ //Write the type into the data parameter, and the headers will be automatically inferred, so you don’t need to specify the type.
+ transformData(data: Todo[], headers) {
+ return data.map(item => ({
+ ...item,
+ status: item.done ? 'Completed' : 'Not completed'
+ }));
+ }
+});
+
+const { data } = useRequest(Get);
+// vue: The type of data is Ref<(Todo & { status: string })[]>
+// react: The type of data is (Todo & { status: string })[]
+// svelte: The type of data is Writable<(Todo & { status: string })[]>
+```
+
+:::warning note
+
+The response data is converted by the global response interceptor, so when setting the type, it should also be set to the converted type.
+
+:::
+
+## Type inferred from request adapter
+
+Because alova supports custom request adapters, and the request configuration objects, response objects, and response headers of different adapters may be different, so the global `beforeRequest`, `responded` interceptors, and the configuration object when the `Method` instance is created The types will be automatically inferred based on the types provided by the request adapter. Let's look at these types first.
+
+If you are using [**GlobalFetch**](https://github.com/alovajs/alova/blob/main/src/predefine/GlobalFetch.ts), alova will automatically infer the type using `fetch api`, The types of fetch api are as follows.
+
+```typescript
+declare function fetch(input: RequestInfo | URL, init?: RequestInit): Promise;
+```
+
+### Method configuration type of instance
+
+The method configuration type will be automatically inferred as:
+
+```typescript
+// AlovaMethodCommonConfig is a unified request parameter and behavior parameter
+// highlight-start
+const methodConfig: AlovaMethodCommonConfig & RequestInit = {
+ // highlight-end
+ // ...
+};
+alovaInstance.Get('/api/user', methodConfig);
+```
+
+### Global response interceptor parameter type
+
+The type of responded interceptor will be automatically inferred as:
+
+```typescript
+createAlova({
+ // ...
+ // highlight-start
+ responded: (response: Response, method: Method) => {
+ // highlight-end
+ // ...
+ }
+});
+```
diff --git a/docs/tutorial/03-combine-framework/README.md b/versioned_docs/version-2.x/tutorial/03-combine-framework/README.md
similarity index 100%
rename from docs/tutorial/03-combine-framework/README.md
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/README.md
diff --git a/docs/tutorial/03-combine-framework/_category_.json b/versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json
similarity index 92%
rename from docs/tutorial/03-combine-framework/_category_.json
rename to versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json
index 1e1d1ef7f..109e5b140 100644
--- a/docs/tutorial/03-combine-framework/_category_.json
+++ b/versioned_docs/version-2.x/tutorial/03-combine-framework/_category_.json
@@ -1,3 +1,3 @@
-{
- "label": "Combine Framework"
-}
+{
+ "label": "Combine Framework"
+}
diff --git a/docs/tutorial/04-cache/01-mode.md b/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md
similarity index 97%
rename from docs/tutorial/04-cache/01-mode.md
rename to versioned_docs/version-2.x/tutorial/04-cache/01-mode.md
index 175ffc3fe..7fe8e640a 100644
--- a/docs/tutorial/04-cache/01-mode.md
+++ b/versioned_docs/version-2.x/tutorial/04-cache/01-mode.md
@@ -1,215 +1,214 @@
----
-title: Cache Mode
-sidebar_position: 10
----
-
-import MemoryCache from '@site/example-links/MemoryCache';
-import StoragePlaceholder from '@site/example-links/StoragePlaceholder';
-import StorageRestore from '@site/example-links/StorageRestore';
-
-The cache mode can be set at different granularities such as global or request level. When set globally, all Method instances created from the same alova instance will inherit the setting.
-
-:::info note
-
-Whether to use the cache mode and which cache mode to use depends on the scenario. The usage scenarios of different cache modes will be mentioned below when introducing different cache modes separately.
-
-:::
-
-## Memory mode (default)
-
-The memory mode puts the cache in the memory, which means that the page cache is invalidated when it is refreshed, and is the most commonly used cache mode.
-
-Memory mode is generally used to solve the performance consumption caused by frequent requests for the same data in a short period of time (minutes or seconds). For example, when you are writing a todo details page, you may think that users will frequently click on the todo list Check the details, if the user does not repeatedly request the interface when repeatedly viewing a certain detail, and can return the data immediately, the colleague who improves the response speed also reduces the pressure on the server. At this point we can set the response data cache for a todo detail `Method` instance.
-
-```javascript
-alovaInstance.GET('/todo/list', {
- //...
- // highlight-start
- localCache: {
- // Set cache mode to memory mode
- mode: 'memory',
-
- // unit is milliseconds
- // When set to `Infinity`, it means that the data will never expire, and when it is set to 0 or a negative number, it means no caching
- expire: 60 * 10 * 1000
- }
- // highlight-end
-});
-```
-
-Memory mode is the default mode, you can abbreviate like this
-
-```javascript
-alovaInstance.GET('/todo/list', {
- //...
- // highlight-start
- localCache: 60 * 10 * 1000
- // highlight-end
-});
-```
-
-> GET requests will set the memory cache time of 300000ms (5 minutes) by default, and developers can also customize the settings.
-
-> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode)
-
-### Memory mode example
-
-
-
-## Cache placeholder mode
-
-This cache mode is used when you don't want to display the Loading icon every time the application is entered, but you want to use the old data instead, you can use the cache occupancy mode, which has a better experience than Loading.
-
-In the cache occupancy mode, `data` will be immediately assigned the old data of the last cache. You can judge that if there is old data, use it to replace the Loading display. At the same time, it will send a request to obtain the latest data and update the cache, so as to achieve In order to quickly display the actual data, and obtain the latest data.
-
-Set on `Method` instances:
-
-```javascript
-const todoListGetter = alovaInstance.Get('/todo/list', {
- //...
- // highlight-start
- localCache: {
- // Set the cache mode to persistent placeholder mode
- mode: 'placeholder',
- // cache time
- expire: 60 * 10 * 1000
- }
- // highlight-end
-});
-```
-
-> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode)
-
-### Cache placeholder mode example
-
-
-
-## Restore mode
-
-In this mode, the server-side cached data will be persistent. If the expiration time is not reached, even if the page cache is refreshed, it will not be invalidated. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of annual holidays. It is different, but it will not change again. In this scenario, we only need to set the cache expiration time to the last moment of this year.
-
-Set on `Method` instances:
-
-```javascript
-const todoListGetter = alovaInstance.Get('/todo/list', {
- //...
- // highlight-start
- localCache: {
- // Set the cache mode to persistent mode
- mode: 'restore',
- // cache time
- expire: 60 * 10 * 1000
- }
- // highlight-end
-});
-```
-
-:::warning Caution
-
-When request body is special data such as **FormData**, **Blob**, **ArrayBuffer**, **URLSearchParams**, **ReadableStream**, it will be considered that you intend to communicate with server. In this case would not cache data.
-
-:::
-
-> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode)
-
-### Restore mode example
-
-
-
-### What should I do if the data changes in restore mode?
-
-When the `Method` instance in restore mode is set, it may be due to the change of the interface data or the logic change of the front-end processing response data. At this time, it is necessary to let the user re-cache the changed data after publishing the application. At this time, you can use `tag` The attribute sets the cache tag. Each piece of persistent data contains a `tag` identifier. When the `tag` changes, the original persistent data will become invalid, and new data will be obtained again, and the new `tag` will be used for identification .
-
-```javascript
-const todoListGetter = alovaInstance.Get('/todo/list', {
- //...
- localCache: {
- mode: 'restore',
- expire: 60 * 10 * 1000,
-
- // highlight-start
- // Add or modify the tag parameter, the cached data will be invalid
- // It is recommended to use version number management
- tag: 'v1'
- // highlight-end
- }
-});
-```
-
-## Global setting cache mode
-
-:::info version required
-v1.3.0+
-:::
-The above settings are all set separately on `Method`. If you need to set the cache mode globally, you can do it as follows:
-
-```javascript
-const alovaInstance = createAlova({
- //...
- // highlight-start
- localCache: {
- // Uniformly set the cache mode of POST
- POST: {
- mode: 'placeholder',
- expire: 60 * 10 * 1000
- },
- // Uniformly set the cache mode of the HEAD request
- HEAD: 60 * 10 * 1000
- }
- // highlight-end
-});
-```
-
-Henceforth, the `Method` instance created by `alovaInstance` instance will use this cache setting by default, and it can also be overridden in the `Method` instance.
-
-> Note: When the cache mode is set globally, the original 5-minute GET cache mode will be overwritten.
-
-## Disable caching mode globally
-
-If you don't want to use any request cache in your project, you can turn it off globally. If you want to use it only in a few specific requests, you can also turn it off globally and set it in the specified `Method` instance .
-
-```javascript
-const alovaInstance = createAlova({
- //...
- // highlight-start
- // Set to null to disable all request caching globally
- localCache: null
- // highlight-end
-});
-```
-
-## Expiration time type
-
-There are two types of expiration time to choose from, namely **relative time** and **absolute time**
-
-### Relative Time
-
-That is, the time to expire when the cached data is saved, in **milliseconds**, the above examples are all of this type.
-
-```javascript
-localCache: 60 * 10 * 1000;
-```
-
-```javascript
-localCache: {
-expire: 60 * 10 * 1000,
-}
-```
-
-### absolute time
-
-With a specific time point as the expiration time, the cache will expire at the set time point
-
-```javascript
-localCache: new Date('2030-01-01');
-```
-
-```javascript
-localCache: {
- expire: new Date('2030-01-01');
-}
-```
-
-## Instruction for response automatic maintenance
-
-The key of the response data cache is uniquely identified by the combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance. Any information or Different positions will be treated as different keys.
+---
+title: Cache Mode
+---
+
+import MemoryCache from '@site/example-links/MemoryCache';
+import StoragePlaceholder from '@site/example-links/StoragePlaceholder';
+import StorageRestore from '@site/example-links/StorageRestore';
+
+The cache mode can be set at different granularities such as global or request level. When set globally, all Method instances created from the same alova instance will inherit the setting.
+
+:::info note
+
+Whether to use the cache mode and which cache mode to use depends on the scenario. The usage scenarios of different cache modes will be mentioned below when introducing different cache modes separately.
+
+:::
+
+## Memory mode (default)
+
+The memory mode puts the cache in the memory, which means that the page cache is invalidated when it is refreshed, and is the most commonly used cache mode.
+
+Memory mode is generally used to solve the performance consumption caused by frequent requests for the same data in a short period of time (minutes or seconds). For example, when you are writing a todo details page, you may think that users will frequently click on the todo list Check the details, if the user does not repeatedly request the interface when repeatedly viewing a certain detail, and can return the data immediately, the colleague who improves the response speed also reduces the pressure on the server. At this point we can set the response data cache for a todo detail `Method` instance.
+
+```javascript
+alovaInstance.GET('/todo/list', {
+ //...
+ // highlight-start
+ localCache: {
+ // Set cache mode to memory mode
+ mode: 'memory',
+
+ // unit is milliseconds
+ // When set to `Infinity`, it means that the data will never expire, and when it is set to 0 or a negative number, it means no caching
+ expire: 60 * 10 * 1000
+ }
+ // highlight-end
+});
+```
+
+Memory mode is the default mode, you can abbreviate like this
+
+```javascript
+alovaInstance.GET('/todo/list', {
+ //...
+ // highlight-start
+ localCache: 60 * 10 * 1000
+ // highlight-end
+});
+```
+
+> GET requests will set the memory cache time of 300000ms (5 minutes) by default, and developers can also customize the settings.
+
+> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode)
+
+### Memory mode example
+
+
+
+## Cache placeholder mode
+
+This cache mode is used when you don't want to display the Loading icon every time the application is entered, but you want to use the old data instead, you can use the cache occupancy mode, which has a better experience than Loading.
+
+In the cache occupancy mode, `data` will be immediately assigned the old data of the last cache. You can judge that if there is old data, use it to replace the Loading display. At the same time, it will send a request to obtain the latest data and update the cache, so as to achieve In order to quickly display the actual data, and obtain the latest data.
+
+Set on `Method` instances:
+
+```javascript
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ //...
+ // highlight-start
+ localCache: {
+ // Set the cache mode to persistent placeholder mode
+ mode: 'placeholder',
+ // cache time
+ expire: 60 * 10 * 1000
+ }
+ // highlight-end
+});
+```
+
+> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode)
+
+### Cache placeholder mode example
+
+
+
+## Restore mode
+
+In this mode, the server-side cached data will be persistent. If the expiration time is not reached, even if the page cache is refreshed, it will not be invalidated. It is generally used for some data that requires server-side management but is basically unchanged, such as the specific dates of annual holidays. It is different, but it will not change again. In this scenario, we only need to set the cache expiration time to the last moment of this year.
+
+Set on `Method` instances:
+
+```javascript
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ //...
+ // highlight-start
+ localCache: {
+ // Set the cache mode to persistent mode
+ mode: 'restore',
+ // cache time
+ expire: 60 * 10 * 1000
+ }
+ // highlight-end
+});
+```
+
+:::warning Caution
+
+When request body is special data such as **FormData**, **Blob**, **ArrayBuffer**, **URLSearchParams**, **ReadableStream**, it will be considered that you intend to communicate with server. In this case would not cache data.
+
+:::
+
+> If you need to set the caching mode globally, see [Global setting cache mode] at the bottom of this section (#Global setting cache mode)
+
+### Restore mode example
+
+
+
+### What should I do if the data changes in restore mode?
+
+When the `Method` instance in restore mode is set, it may be due to the change of the interface data or the logic change of the front-end processing response data. At this time, it is necessary to let the user re-cache the changed data after publishing the application. At this time, you can use `tag` The attribute sets the cache tag. Each piece of persistent data contains a `tag` identifier. When the `tag` changes, the original persistent data will become invalid, and new data will be obtained again, and the new `tag` will be used for identification .
+
+```javascript
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ //...
+ localCache: {
+ mode: 'restore',
+ expire: 60 * 10 * 1000,
+
+ // highlight-start
+ // Add or modify the tag parameter, the cached data will be invalid
+ // It is recommended to use version number management
+ tag: 'v1'
+ // highlight-end
+ }
+});
+```
+
+## Global setting cache mode
+
+:::info version required
+v1.3.0+
+:::
+The above settings are all set separately on `Method`. If you need to set the cache mode globally, you can do it as follows:
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ // highlight-start
+ localCache: {
+ // Uniformly set the cache mode of POST
+ POST: {
+ mode: 'placeholder',
+ expire: 60 * 10 * 1000
+ },
+ // Uniformly set the cache mode of the HEAD request
+ HEAD: 60 * 10 * 1000
+ }
+ // highlight-end
+});
+```
+
+Henceforth, the `Method` instance created by `alovaInstance` instance will use this cache setting by default, and it can also be overridden in the `Method` instance.
+
+> Note: When the cache mode is set globally, the original 5-minute GET cache mode will be overwritten.
+
+## Disable caching mode globally
+
+If you don't want to use any request cache in your project, you can turn it off globally. If you want to use it only in a few specific requests, you can also turn it off globally and set it in the specified `Method` instance .
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ // highlight-start
+ // Set to null to disable all request caching globally
+ localCache: null
+ // highlight-end
+});
+```
+
+## Expiration time type
+
+There are two types of expiration time to choose from, namely **relative time** and **absolute time**
+
+### Relative Time
+
+That is, the time to expire when the cached data is saved, in **milliseconds**, the above examples are all of this type.
+
+```javascript
+localCache: 60 * 10 * 1000;
+```
+
+```javascript
+localCache: {
+expire: 60 * 10 * 1000,
+}
+```
+
+### absolute time
+
+With a specific time point as the expiration time, the cache will expire at the set time point
+
+```javascript
+localCache: new Date('2030-01-01');
+```
+
+```javascript
+localCache: {
+ expire: new Date('2030-01-01');
+}
+```
+
+## Instruction for response automatic maintenance
+
+The key of the response data cache is uniquely identified by the combination of the request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody) of the method instance. Any information or Different positions will be treated as different keys.
diff --git a/docs/tutorial/04-cache/02-auto-invalidate.md b/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md
similarity index 97%
rename from docs/tutorial/04-cache/02-auto-invalidate.md
rename to versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md
index 9bbdfe62c..a9b8fbeb8 100644
--- a/docs/tutorial/04-cache/02-auto-invalidate.md
+++ b/versioned_docs/version-2.x/tutorial/04-cache/02-auto-invalidate.md
@@ -1,91 +1,90 @@
----
-title: Auto Invalidate
-sidebar_position: 20
----
-
-There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated with the edited content. Usually The approach is to trigger content updates on the previous page through events, which increases maintenance costs. And `alova` provides 3 ways to achieve this goal very elegantly:
-
-1. Use `useFetcher` to immediately re-request the latest data, which will be explained in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter;
-2. Update the cache. This method will be explained in detail in the [Cache set and query](/tutorial/cache/set-and-query) chapter later;
-3. Invalidate the response cache. When requested again, the data will be requested again due to cache invalidation. This is also what this chapter will explain.
-
-Automatic cache invalidation is to set invalidation source rules in the target cache. As long as the rules match, the target cache can be automatically invalidated. This saves the trouble of manually clearing the cache in many cases.
-
-## scenes to be used
-
-Setting up automatic invalidation rules is convenient when the target cache is one-to-one or one-to-many with the invalidation source.
-
-```mermaid
-flowchart
- M1[method1 invalidation source points to] --> T1[target cache]
- M11[method1 invalidation source points to] --> T2[target cache]
- M2[method2 invalidation source points to] --> T2[target cache]
- MN[methodN invalidation source points to] --> T2[target cache]
-```
-
-## Set automatic invalidation rules
-
-Setting this rule is very simple, you can set the `hitSource` parameter for it when creating a Method instance with caching.
-
-### Invalidation source is set to method instance
-
-With a fixed method instance as the invalidation source, as long as the method instance or its clone instance succeeds, the target cache will be cleared automatically.
-
-```javascript
-alova.Get('/todo/1', {
- //...
- hitSource: alova.Post('/todo', {})
-});
-```
-
-### Match invalidating source by method name
-
-Like the method instance matcher, you can specify the name of the method in hitSource to match the failure source. Multiple failure sources can be set to the same name. When the method instance request with this name succeeds, the target cache will be automatically cleared.
-
-```javascript
-const methodSubmitTodo = data =>
- alova.Post('/todo', data, {
- name: 'submitTodo'
- });
-
-alova.Get('/todo/1', {
- //...
- // Match the failure source whose method instance name is submitTodo
- hitSource: 'submitTodo'
-});
-```
-
-### Match invalidating source by regexp of method name
-
-If the method instance name is not fixed, you can specify a regular expression in hitSource to match the method name, and the target cache will be automatically cleared when the matched method instance succeeds in the request.
-
-```javascript
-const methodSubmitTodo = data =>
- alova.Post('/todo', data, {
- name: 'prefix-submitTodo'
- });
-
-alova.Get('/todo/1', {
- //...
- // Match all instances whose method instance name starts with prefix
- hitSource: /^prefix/
-});
-```
-
-### Combination setting validating source
-
-If you want to use the above multiple rules to match failure sources, you can specify hitSource as an array, and the array item is any one of the above three rules, and the method instance that meets any one of the rules in the array will be matched.
-
-```javascript
-alova.Get('/todo/1', {
- //...
- // When the method instance request that satisfies any matching rule in the array succeeds, the cache will be invalidated
- hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/]
-});
-```
-
-## hitSource data type
-
-```typescript
-type hitSource = string | RegExp | Method | (string | RegExp | Method)[];
-```
+---
+title: Auto Invalidate
+---
+
+There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated with the edited content. Usually The approach is to trigger content updates on the previous page through events, which increases maintenance costs. And `alova` provides 3 ways to achieve this goal very elegantly:
+
+1. Use `useFetcher` to immediately re-request the latest data, which will be explained in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter;
+2. Update the cache. This method will be explained in detail in the [Cache set and query](/tutorial/cache/set-and-query) chapter later;
+3. Invalidate the response cache. When requested again, the data will be requested again due to cache invalidation. This is also what this chapter will explain.
+
+Automatic cache invalidation is to set invalidation source rules in the target cache. As long as the rules match, the target cache can be automatically invalidated. This saves the trouble of manually clearing the cache in many cases.
+
+## scenes to be used
+
+Setting up automatic invalidation rules is convenient when the target cache is one-to-one or one-to-many with the invalidation source.
+
+```mermaid
+flowchart
+ M1[method1 invalidation source points to] --> T1[target cache]
+ M11[method1 invalidation source points to] --> T2[target cache]
+ M2[method2 invalidation source points to] --> T2[target cache]
+ MN[methodN invalidation source points to] --> T2[target cache]
+```
+
+## Set automatic invalidation rules
+
+Setting this rule is very simple, you can set the `hitSource` parameter for it when creating a Method instance with caching.
+
+### Invalidation source is set to method instance
+
+With a fixed method instance as the invalidation source, as long as the method instance or its clone instance succeeds, the target cache will be cleared automatically.
+
+```javascript
+alova.Get('/todo/1', {
+ //...
+ hitSource: alova.Post('/todo', {})
+});
+```
+
+### Match invalidating source by method name
+
+Like the method instance matcher, you can specify the name of the method in hitSource to match the failure source. Multiple failure sources can be set to the same name. When the method instance request with this name succeeds, the target cache will be automatically cleared.
+
+```javascript
+const methodSubmitTodo = data =>
+ alova.Post('/todo', data, {
+ name: 'submitTodo'
+ });
+
+alova.Get('/todo/1', {
+ //...
+ // Match the failure source whose method instance name is submitTodo
+ hitSource: 'submitTodo'
+});
+```
+
+### Match invalidating source by regexp of method name
+
+If the method instance name is not fixed, you can specify a regular expression in hitSource to match the method name, and the target cache will be automatically cleared when the matched method instance succeeds in the request.
+
+```javascript
+const methodSubmitTodo = data =>
+ alova.Post('/todo', data, {
+ name: 'prefix-submitTodo'
+ });
+
+alova.Get('/todo/1', {
+ //...
+ // Match all instances whose method instance name starts with prefix
+ hitSource: /^prefix/
+});
+```
+
+### Combination setting validating source
+
+If you want to use the above multiple rules to match failure sources, you can specify hitSource as an array, and the array item is any one of the above three rules, and the method instance that meets any one of the rules in the array will be matched.
+
+```javascript
+alova.Get('/todo/1', {
+ //...
+ // When the method instance request that satisfies any matching rule in the array succeeds, the cache will be invalidated
+ hitSource: [alova.Post('/todo', {}), 'submitTodo', /^prefix/]
+});
+```
+
+## hitSource data type
+
+```typescript
+type hitSource = string | RegExp | Method | (string | RegExp | Method)[];
+```
diff --git a/docs/tutorial/04-cache/03-manually-invalidate.md b/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md
similarity index 96%
rename from docs/tutorial/04-cache/03-manually-invalidate.md
rename to versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md
index bd8dd546b..1dbaa8199 100644
--- a/docs/tutorial/04-cache/03-manually-invalidate.md
+++ b/versioned_docs/version-2.x/tutorial/04-cache/03-manually-invalidate.md
@@ -1,102 +1,101 @@
----
-title: Manually Invalidate
-sidebar_position: 30
----
-
-Generally, automatic invalidate cache is more concise, and it is recommended to use it first to invalidate the cache. When the automatic invalidate cache does not meet the needs, you can also invalidate the cache by calling `invalidateCache`.
-
-## Use method instance invalidate cache
-
-Pass in a method instance in the `invalidateCache` function, it will look for the cache under this instance to invalidate it.
-
-In the following example, when the submission is successful, the todo details data cache will be invalidated.
-
-```javascript
-// Get the todo details data with id 1
-const getTodoDetail = id =>
- alovaInstance.Get(`/todo/${id}`, {
- localCache: 1000000
- });
-const { loading, data } = useRequest(getTodoDetail(1));
-```
-
-```javascript
-// Submit the data and invalidate the todo details with id 1.
-const {
- // ...
- send,
- onSuccess
-} = useRequest(createTodoPoster, { immediate: false });
-
-// highlight-start
-//Invalid cache after successful submission
-onSuccess(() => {
- invalidateCache(getTodoDetail(1));
-});
-// highlight-end
-
-const handleSubmit = () => {
- send({
- title: 'new todo',
- content: 'new todo content'
- });
-};
-```
-
-## Batch invalidate cache
-
-In the following example, we invalidate caches in batches by specifying the name of the cache or a regular expression of the name.
-
-```javascript
-//The cache of the method named todoList will be invalidated
-invalidateCache('todoList');
-
-// The cache of methods whose names match the following regular expression will be invalidated
-invalidateCache(/^todoList/);
-```
-
-## Dynamic invalidate cache
-
-Maybe sometimes you are not sure which cached data needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first 5 method instances named todoList.
-
-```javascript
-const getTodoList = currentPage => {
- return alovaInstance.Get('/todo/list', {
- // highlight-start
- // First set the name for the method instance, which is used to filter out the required Method instance when the Method instance cannot be specified directly.
- name: 'todoList',
- // highlight-end
- params: {
- currentPage,
- pageSize: 10
- }
- });
-};
-
-const {
- // ...
- send,
- onSuccess
-} = useRequest(createTodoPoster, { immediate: false });
-// After successful submission, the todo data cache of the first page will be invalidated.
-onSuccess(() => {
- // highlight-start
- //The cache of the first 5 Method instances whose invalid name is todoList
- invalidateCache({
- name: 'todoList',
- filter: (method, index, ary) => {
- return index < 5;
- }
- });
- // highlight-end
-});
-```
-
-> For more methods of using method instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher)
-
-## Invalidate all caches
-
-```javascript
-// When no parameters are passed, all response caches are invalidated
-invalidateCache();
-```
+---
+title: Manually Invalidate
+---
+
+Generally, automatic invalidate cache is more concise, and it is recommended to use it first to invalidate the cache. When the automatic invalidate cache does not meet the needs, you can also invalidate the cache by calling `invalidateCache`.
+
+## Use method instance invalidate cache
+
+Pass in a method instance in the `invalidateCache` function, it will look for the cache under this instance to invalidate it.
+
+In the following example, when the submission is successful, the todo details data cache will be invalidated.
+
+```javascript
+// Get the todo details data with id 1
+const getTodoDetail = id =>
+ alovaInstance.Get(`/todo/${id}`, {
+ localCache: 1000000
+ });
+const { loading, data } = useRequest(getTodoDetail(1));
+```
+
+```javascript
+// Submit the data and invalidate the todo details with id 1.
+const {
+ // ...
+ send,
+ onSuccess
+} = useRequest(createTodoPoster, { immediate: false });
+
+// highlight-start
+//Invalid cache after successful submission
+onSuccess(() => {
+ invalidateCache(getTodoDetail(1));
+});
+// highlight-end
+
+const handleSubmit = () => {
+ send({
+ title: 'new todo',
+ content: 'new todo content'
+ });
+};
+```
+
+## Batch invalidate cache
+
+In the following example, we invalidate caches in batches by specifying the name of the cache or a regular expression of the name.
+
+```javascript
+//The cache of the method named todoList will be invalidated
+invalidateCache('todoList');
+
+// The cache of methods whose names match the following regular expression will be invalidated
+invalidateCache(/^todoList/);
+```
+
+## Dynamic invalidate cache
+
+Maybe sometimes you are not sure which cached data needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically find the corresponding method instance. The following example shows how to invalidate the cache of the first 5 method instances named todoList.
+
+```javascript
+const getTodoList = currentPage => {
+ return alovaInstance.Get('/todo/list', {
+ // highlight-start
+ // First set the name for the method instance, which is used to filter out the required Method instance when the Method instance cannot be specified directly.
+ name: 'todoList',
+ // highlight-end
+ params: {
+ currentPage,
+ pageSize: 10
+ }
+ });
+};
+
+const {
+ // ...
+ send,
+ onSuccess
+} = useRequest(createTodoPoster, { immediate: false });
+// After successful submission, the todo data cache of the first page will be invalidated.
+onSuccess(() => {
+ // highlight-start
+ //The cache of the first 5 Method instances whose invalid name is todoList
+ invalidateCache({
+ name: 'todoList',
+ filter: (method, index, ary) => {
+ return index < 5;
+ }
+ });
+ // highlight-end
+});
+```
+
+> For more methods of using method instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher)
+
+## Invalidate all caches
+
+```javascript
+// When no parameters are passed, all response caches are invalidated
+invalidateCache();
+```
diff --git a/docs/tutorial/04-cache/04-force-request.md b/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md
similarity index 94%
rename from docs/tutorial/04-cache/04-force-request.md
rename to versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md
index 3337a47a8..4c5a75861 100644
--- a/docs/tutorial/04-cache/04-force-request.md
+++ b/versioned_docs/version-2.x/tutorial/04-cache/04-force-request.md
@@ -1,75 +1,74 @@
----
-title: Force Request
-sidebar_position: 40
----
-
-Forced request refers to a mechanism that bypasses the cache check and triggers a request. It is useful when the latest data needs to be obtained under certain conditions.
-
-## Force request with method
-
-Force request by calling the send function of the method instance, and passing `true`.
-
-```javascript
-const response = await alovaInstance.Get('/api/user').send(true);
-```
-
-## Force request in useHook
-
-Among the three core hooks of `useRequest/useWatcher/useFetcher`, force request parameters are supported.
-
-```javascript
-// useRequest
-useRequest(todoListGetter, {
- // highlight-start
- force: true
- // highlight-end
-});
-
-// useWatcher
-useWatcher(todoListGetter, [page], {
- // highlight-start
- force: true
- // highlight-end
-});
-
-// useFetcher
-useFetcher({
- // highlight-start
- force: true
- // highlight-end
-});
-```
-
-### Dynamically set force value
-
-In actual situations, we often need to set whether to force the request to be sent based on different situations. In this case, force can be set to a function, which will also receive parameters passed in from the `send` function. Please read the [receive params](/tutorial/combine-framework/receive-params) for details
-
-```javascript
-useRequest(todoListGetter, {
- // highlight-start
- force: isForce => {
- return isForce;
- }
- // highlight-end
-});
-
-// useWatcher
-useWatcher(todoListGetter, [page], {
- // highlight-start
- force: isForce => {
- return isForce;
- }
- // highlight-end
-});
-
-// useFetcher
-useFetcher({
- // highlight-start
- force: isForce => {
- return isForce;
- }
- // highlight-end
-});
-```
-
-`useFetcher` is a useHook for data fetching, which will be discussed in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter later.
+---
+title: Force Request
+---
+
+Forced request refers to a mechanism that bypasses the cache check and triggers a request. It is useful when the latest data needs to be obtained under certain conditions.
+
+## Force request with method
+
+Force request by calling the send function of the method instance, and passing `true`.
+
+```javascript
+const response = await alovaInstance.Get('/api/user').send(true);
+```
+
+## Force request in useHook
+
+Among the three core hooks of `useRequest/useWatcher/useFetcher`, force request parameters are supported.
+
+```javascript
+// useRequest
+useRequest(todoListGetter, {
+ // highlight-start
+ force: true
+ // highlight-end
+});
+
+// useWatcher
+useWatcher(todoListGetter, [page], {
+ // highlight-start
+ force: true
+ // highlight-end
+});
+
+// useFetcher
+useFetcher({
+ // highlight-start
+ force: true
+ // highlight-end
+});
+```
+
+### Dynamically set force value
+
+In actual situations, we often need to set whether to force the request to be sent based on different situations. In this case, force can be set to a function, which will also receive parameters passed in from the `send` function. Please read the [receive params](/tutorial/combine-framework/receive-params) for details
+
+```javascript
+useRequest(todoListGetter, {
+ // highlight-start
+ force: isForce => {
+ return isForce;
+ }
+ // highlight-end
+});
+
+// useWatcher
+useWatcher(todoListGetter, [page], {
+ // highlight-start
+ force: isForce => {
+ return isForce;
+ }
+ // highlight-end
+});
+
+// useFetcher
+useFetcher({
+ // highlight-start
+ force: isForce => {
+ return isForce;
+ }
+ // highlight-end
+});
+```
+
+`useFetcher` is a useHook for data fetching, which will be discussed in the [Data Fetching](/tutorial/advanced/use-fetcher) chapter later.
diff --git a/docs/tutorial/04-cache/05-set-and-query.md b/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md
similarity index 94%
rename from docs/tutorial/04-cache/05-set-and-query.md
rename to versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md
index c11798832..bfaf6d68a 100644
--- a/docs/tutorial/04-cache/05-set-and-query.md
+++ b/versioned_docs/version-2.x/tutorial/04-cache/05-set-and-query.md
@@ -1,284 +1,295 @@
----
-title: Set & Query Cache
-sidebar_position: 50
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-The cache also supports updating and querying, As we mentioned in [cache mode](/tutorial/cache/mode), each cached data is saved with the method instance that sends the request as the key, so the method instance will also be used when updating the cache manually to find the corresponding cached data.
-
-## Update static cache data
-
-
-
-
-```html
-
-
-
-
-```
-
-
-
-
-```jsx
-import { setCache } from 'alova';
-import { useState } from 'react';
-
-const getTodoListByDate = dateList =>
- alovaInstance.Get('/todo/list/dates', {
- params: { dateList }
- });
-
-const App = () => {
- // Get 5 days of data in batches during initialization
- const [dates, setDates] = useState(['2022-05-01', '2022-05-02', '2022-05-03', '2022-05-04', '2022-05-05']);
- const {
- //...
- onSuccess
- } = useWatcher(() => getTodoListByDate(dates.join()), [dates], {
- immediate: true
- });
- onSuccess(todoListDates => {
- if (todoListDates.length <= 1) {
- return;
- }
-
- // highlight-start
- // By default, the data of these 5 days will be cached together in a key
- // In order to make subsequent requests for data of a certain day also hit the cache, we can disassemble the data of 5 days into days, and manually set the response cache one by one through setCache
- // The first parameter of setCache is the method instance object, which is used to specify the key of the cache
- // The second parameter is the cached data
- todoListDates.forEach(todoDate => {
- setCache(getTodoListByDate(todoDate.date), [todoDate]);
- });
- // highlight-end
- });
-
- // highlight-start
- const handleTodolistToggle = () => {
- // At this time, when the switching date is May 1, it will hit the response cache we manually set.
- // The dates value is being monitored by useWatcher, so changing it can automatically trigger the request
- setDates(['2022-05-01']);
- };
- // highlight-end
-
- return ;
-};
-```
-
-
-
-
-```html
-
-
-```
-
-
-
-
-```html
-
-
-
-
-```
-
-
-
-
-## Dynamically set cache data
-
-You can also pass in a callback function in `setCache` to dynamically calculate the cache data and return the cache data that needs to be updated.
-
-```javascript
-setCache(getTodoListByDate('2022-10-01'), oldCache => {
- // Return the data that needs to be cached
- return {
- ...oldCache,
- expire: isAfter('2022-10-01', new Date())
- };
-});
-```
-
-Similarly, you can also dynamically find method instances through [method instance matcher](/tutorial/advanced/method-matcher).
-
-```javascript
-setCache(
- {
- name: 'todoList',
- filter: (method, index, ary) => {
- return index < 5;
- }
- },
- 'newCache'
-);
-```
-
-## Abort to set cache
-
-Sometimes you need to dynamically determine whether to update the cache. If no data is returned in the callback function of `setCache`, or `undefined` is returned, the original cache data will not be updated at this time
-
-```javascript
-setCache(getTodoListByDate('2022-10-01'), oldCache => {
- const isExpired = isAfter('2022-10-01', new Date());
- if (!isExpired) {
- return; // abort cache updating when return the undefined
- }
- return null; // update the cache to null
-});
-```
-
-## Cache query
-
-At the same time, we also provide a cache query method.
-
-```javascript
-import { queryCache } from 'alova';
-
-const cacheData = queryCache(getTodoListByDate('2022-10-01'));
-```
-
-You can also dynamically find method instances via [method instance matcher](/tutorial/advanced/method-matcher).
-
-```javascript
-const cacheData = queryCache({
- name: 'todoList',
- filter: (method, index, ary) => {
- return index < 5;
- }
-});
-```
+---
+title: Set & Query Cache
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+The cache also supports updating and querying, As we mentioned in [cache mode](/tutorial/cache/mode), each cached data is saved with the method instance that sends the request as the key, so the method instance will also be used when updating the cache manually to find the corresponding cached data.
+
+## Update static cache data
+
+
+
+
+```html
+
+
+
+
+```
+
+
+
+
+```jsx
+import { setCache } from 'alova';
+import { useState } from 'react';
+
+const getTodoListByDate = dateList =>
+ alovaInstance.Get('/todo/list/dates', {
+ params: { dateList }
+ });
+
+const App = () => {
+ // Get 5 days of data in batches during initialization
+ const [dates, setDates] = useState([
+ '2022-05-01',
+ '2022-05-02',
+ '2022-05-03',
+ '2022-05-04',
+ '2022-05-05'
+ ]);
+ const {
+ //...
+ onSuccess
+ } = useWatcher(() => getTodoListByDate(dates.join()), [dates], {
+ immediate: true
+ });
+ onSuccess(todoListDates => {
+ if (todoListDates.length <= 1) {
+ return;
+ }
+
+ // highlight-start
+ // By default, the data of these 5 days will be cached together in a key
+ // In order to make subsequent requests for data of a certain day also hit the cache, we can disassemble the data of 5 days into days, and manually set the response cache one by one through setCache
+ // The first parameter of setCache is the method instance object, which is used to specify the key of the cache
+ // The second parameter is the cached data
+ todoListDates.forEach(todoDate => {
+ setCache(getTodoListByDate(todoDate.date), [todoDate]);
+ });
+ // highlight-end
+ });
+
+ // highlight-start
+ const handleTodolistToggle = () => {
+ // At this time, when the switching date is May 1, it will hit the response cache we manually set.
+ // The dates value is being monitored by useWatcher, so changing it can automatically trigger the request
+ setDates(['2022-05-01']);
+ };
+ // highlight-end
+
+ return ;
+};
+```
+
+
+
+
+```html
+
+
+```
+
+
+
+
+```html
+
+
+
+
+```
+
+
+
+
+## Dynamically set cache data
+
+You can also pass in a callback function in `setCache` to dynamically calculate the cache data and return the cache data that needs to be updated.
+
+```javascript
+setCache(getTodoListByDate('2022-10-01'), oldCache => {
+ // Return the data that needs to be cached
+ return {
+ ...oldCache,
+ expire: isAfter('2022-10-01', new Date())
+ };
+});
+```
+
+Similarly, you can also dynamically find method instances through [method instance matcher](/tutorial/advanced/method-matcher).
+
+```javascript
+setCache(
+ {
+ name: 'todoList',
+ filter: (method, index, ary) => {
+ return index < 5;
+ }
+ },
+ 'newCache'
+);
+```
+
+## Abort to set cache
+
+Sometimes you need to dynamically determine whether to update the cache. If no data is returned in the callback function of `setCache`, or `undefined` is returned, the original cache data will not be updated at this time
+
+```javascript
+setCache(getTodoListByDate('2022-10-01'), oldCache => {
+ const isExpired = isAfter('2022-10-01', new Date());
+ if (!isExpired) {
+ return; // abort cache updating when return the undefined
+ }
+ return null; // update the cache to null
+});
+```
+
+## Cache query
+
+At the same time, we also provide a cache query method.
+
+```javascript
+import { queryCache } from 'alova';
+
+const cacheData = queryCache(getTodoListByDate('2022-10-01'));
+```
+
+You can also dynamically find method instances via [method instance matcher](/tutorial/advanced/method-matcher).
+
+```javascript
+const cacheData = queryCache({
+ name: 'todoList',
+ filter: (method, index, ary) => {
+ return index < 5;
+ }
+});
+```
diff --git a/docs/tutorial/04-cache/06-controlled-cache.md b/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md
similarity index 97%
rename from docs/tutorial/04-cache/06-controlled-cache.md
rename to versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md
index 89dec2a39..62e915556 100644
--- a/docs/tutorial/04-cache/06-controlled-cache.md
+++ b/versioned_docs/version-2.x/tutorial/04-cache/06-controlled-cache.md
@@ -1,83 +1,82 @@
----
-title: Controlled Cache
-sidebar_position: 60
----
-
-:::info version requirements
-
-v2.1.0+
-
-:::
-
-When sending a request, by default it will first check whether there is matching cache data. If it matches, it will use it as the response data to return. If in some scenarios, the user needs to use a custom cache, they must first use `setCache` to synchronize Setting up cached data can only work, which undoubtedly increases the burden on users. This is an uncontrolled cache.
-
-If you want to use **IndexedDB** to custom manage cached data under uncontrolled caching, you may first pre-set the hit cache for the upcoming request, like this:
-
-```javascript
-const getFile = fileName => {
- const fileGetter = alovaInstance.GET(`/file/${fileName}`);
- const tx = db.transaction(['files']);
- const getRequest = tx.objectStore('files').get(fileName);
- getRequest.onsuccess = ({ result }) => {
- setCache(fileGetter, result);
- };
- return fileGetter;
-};
-```
-
-**❌ However, the above writing method is not recommended** for the following reasons:
-
-1. Each time `getFile` is called, a cache will be set, but fileGetter is not necessarily used to send requests;
-2. IndexedDB is an asynchronous interface. If the cache matching step occurs before IndexedDB triggers onsuccess, then the cached data will not be matched, and their order is unpredictable;
-3. Custom cache management tasks and methods are separated, but in fact they should be brought together;
-
-In this case, you can use controlled caching to solve the above problem. Using controlled caching is also very simple. You can set the localCache in the method to an asynchronous or synchronous function, and return custom data as a hit response.
-
-```javascript
-const getFile = fileName =>
- alovaInstance.GET(`/file/${fileName}`, {
- // Controlled cache functions support asynchronous and synchronous functions
- localCache() {
- return new Promise((resolve, reject) => {
- const tx = db.transaction(['files']);
- const getRequest = tx.objectStore('files').get(fileName);
- getRequest.onsuccess = resolve;
- getRequest.onerror = reject;
- });
- }
- });
-```
-
-## Do not use caching
-
-If you wish to continue sending the request, you can return `undefined` or `void` in `localCache`, which is useful in the case of cache misses when customizing the cache.
-
-## Use transformData to set up cache
-
-Because the `transformData` function has the following two properties:
-
-- It is only triggered when responding, but will not be triggered when the response cache is hit;
-- Support asynchronous functions;
-
-Therefore, you can also use it to save customized caches. For example, in a cache scenario where files are used as response data, you can use IndexedDB to cache file data.
-
-```javascript
-const fileGetter = alovaInstance.Get('/file/file_name', {
- // Use IndexedDB to cache files
- async transformData(fileBlob) {
- await new Promise((resolve, reject) => {
- const tx = db.transaction(['files'], 'readwrite');
- const putRequest = tx.objectStore('files').put({
- file: fileBlob
- });
- putRequest.onsuccess = resolve;
- putRequest.onerror = reject;
- });
- return fileBlob;
- }
-});
-```
-
-## Notes
-
-When used in usehooks, throwing an error in the `localCache` function will trigger `onError`. When a request is made directly using a method instance, a promise instance with a reject status will be returned.
+---
+title: Controlled Cache
+---
+
+:::info version requirements
+
+v2.1.0+
+
+:::
+
+When sending a request, by default it will first check whether there is matching cache data. If it matches, it will use it as the response data to return. If in some scenarios, the user needs to use a custom cache, they must first use `setCache` to synchronize Setting up cached data can only work, which undoubtedly increases the burden on users. This is an uncontrolled cache.
+
+If you want to use **IndexedDB** to custom manage cached data under uncontrolled caching, you may first pre-set the hit cache for the upcoming request, like this:
+
+```javascript
+const getFile = fileName => {
+ const fileGetter = alovaInstance.GET(`/file/${fileName}`);
+ const tx = db.transaction(['files']);
+ const getRequest = tx.objectStore('files').get(fileName);
+ getRequest.onsuccess = ({ result }) => {
+ setCache(fileGetter, result);
+ };
+ return fileGetter;
+};
+```
+
+**❌ However, the above writing method is not recommended** for the following reasons:
+
+1. Each time `getFile` is called, a cache will be set, but fileGetter is not necessarily used to send requests;
+2. IndexedDB is an asynchronous interface. If the cache matching step occurs before IndexedDB triggers onsuccess, then the cached data will not be matched, and their order is unpredictable;
+3. Custom cache management tasks and methods are separated, but in fact they should be brought together;
+
+In this case, you can use controlled caching to solve the above problem. Using controlled caching is also very simple. You can set the localCache in the method to an asynchronous or synchronous function, and return custom data as a hit response.
+
+```javascript
+const getFile = fileName =>
+ alovaInstance.GET(`/file/${fileName}`, {
+ // Controlled cache functions support asynchronous and synchronous functions
+ localCache() {
+ return new Promise((resolve, reject) => {
+ const tx = db.transaction(['files']);
+ const getRequest = tx.objectStore('files').get(fileName);
+ getRequest.onsuccess = resolve;
+ getRequest.onerror = reject;
+ });
+ }
+ });
+```
+
+## Do not use caching
+
+If you wish to continue sending the request, you can return `undefined` or `void` in `localCache`, which is useful in the case of cache misses when customizing the cache.
+
+## Use transformData to set up cache
+
+Because the `transformData` function has the following two properties:
+
+- It is only triggered when responding, but will not be triggered when the response cache is hit;
+- Support asynchronous functions;
+
+Therefore, you can also use it to save customized caches. For example, in a cache scenario where files are used as response data, you can use IndexedDB to cache file data.
+
+```javascript
+const fileGetter = alovaInstance.Get('/file/file_name', {
+ // Use IndexedDB to cache files
+ async transformData(fileBlob) {
+ await new Promise((resolve, reject) => {
+ const tx = db.transaction(['files'], 'readwrite');
+ const putRequest = tx.objectStore('files').put({
+ file: fileBlob
+ });
+ putRequest.onsuccess = resolve;
+ putRequest.onerror = reject;
+ });
+ return fileBlob;
+ }
+});
+```
+
+## Notes
+
+When used in usehooks, throwing an error in the `localCache` function will trigger `onError`. When a request is made directly using a method instance, a promise instance with a reject status will be returned.
diff --git a/docs/tutorial/04-cache/README.md b/versioned_docs/version-2.x/tutorial/04-cache/README.md
similarity index 98%
rename from docs/tutorial/04-cache/README.md
rename to versioned_docs/version-2.x/tutorial/04-cache/README.md
index be3949357..e8dbdd057 100644
--- a/docs/tutorial/04-cache/README.md
+++ b/versioned_docs/version-2.x/tutorial/04-cache/README.md
@@ -1,9 +1,9 @@
----
-title: Response Cache
----
-
-Response cache is a technology that caches the data returned by server to the client. It avoids sending repeated requests when the server data can be reused. It can not only respond to user requests immdediately, but also save server resources. According to different caching scenarios, alova provides 3 modes named **memory mode, cache placeholder mode, and restore mode**. Just choose the one that suits you.
-
-In addition, using the cache operation API, you can freely add, modify, and delete caches, as well as customize cache matching rules.
-
-Next, let us start to understand from the cache mode!
+---
+title: Response Cache
+---
+
+Response cache is a technology that caches the data returned by server to the client. It avoids sending repeated requests when the server data can be reused. It can not only respond to user requests immdediately, but also save server resources. According to different caching scenarios, alova provides 3 modes named **memory mode, cache placeholder mode, and restore mode**. Just choose the one that suits you.
+
+In addition, using the cache operation API, you can freely add, modify, and delete caches, as well as customize cache matching rules.
+
+Next, let us start to understand from the cache mode!
diff --git a/docs/tutorial/04-cache/_category_.json b/versioned_docs/version-2.x/tutorial/04-cache/_category_.json
similarity index 91%
rename from docs/tutorial/04-cache/_category_.json
rename to versioned_docs/version-2.x/tutorial/04-cache/_category_.json
index 2360c70d8..1b6419623 100644
--- a/docs/tutorial/04-cache/_category_.json
+++ b/versioned_docs/version-2.x/tutorial/04-cache/_category_.json
@@ -1,3 +1,3 @@
-{
- "label": "Response Cache"
-}
+{
+ "label": "Response Cache"
+}
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md
similarity index 97%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md
index ee27ca5d8..e14e5bd00 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/02-virtual-data.md
@@ -1,177 +1,176 @@
----
-title: Virtual data
-sidebar_position: 20
----
-
-In fact, virtual data is a reference object with a unique id, and its tracking mechanism is realized by first generating a mapping between virtual data id and response data, and then finding and replacing it with the actual value through virtual data id.
-
-When the original value is a reference type, the performance is the same as the original value, but the virtual data of the basic type uses `Number, String, Boolean` encapsulation classes, as well as custom `Undefined, Null` encapsulation classes, and their expressions are the same as There are some deviations from the original value. The characteristics of the virtual data and the use of auxiliary functions for the virtual data are listed below. The details of the auxiliary functions will be introduced at the end of the chapter.
-
-## string concatenation
-
-When virtual data is concatenated, it will be converted to virtual data id for splicing.
-
-```javascript
-const virtualData = createVirtualData(1);
-'a' + virtualData; // a[vd:xxxxxx]
-1 + virtualData; // 1[vd:xxxxxx]
-```
-
-## Data Comparison
-
-Virtual data cannot be directly used for comparison, but virtual data and actual data are often mixed and compared in actual scenarios. In this case, `equals` can be used for comparison.
-
-```javascript
-import { equals } from '@alova/scene-*';
-
-equals('a', 'a'); // true
-
-const virtualData1 = createVirtualData(1);
-const virtualData2 = virtualData1.clone(); // clone virtual data
-equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same
-equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx]
-```
-
-## Participate in operations
-
-When participating in operations such as `+-*/%`, numerical comparison, and bit operations, it cannot be automatically converted to the original value. It can be converted to the original value through `dehydrateVData` and then calculated.
-
-```javascript
-import { dehydrateVData } from '@alova/scene-*';
-
-const virtualData = createVirtualData(1);
-dehydrateVData(virtualData) + 1; // 2
-dehydrateVData(virtualData) > 0; // true
-```
-
-## type operator
-
-Because the virtual data is implemented using the encapsulation class on the basic data type, `object` will always be returned when using `typeof` to get the type, and it can also be converted to the original value by `dehydrateVData` to get the type
-
-```javascript
-const vNum = createVirtualResponse(1);
-typeof vNum === 'object'; // true
-const vUndef = createVirtualResponse(undefined);
-typeof vUndef === 'object'; // true
-
-typeof dehydrateVData(vNum) === 'number'; // true
-typeof dehydrateVData(vUndef) === 'undefined'; // true
-```
-
-To solve this problem, you can use the virtual data helper function **dehydrateVData**, which can get the original value of a virtual data, and return it unchanged when encountering non-virtual data
-
-```javascript
-const vNum = createVirtualResponse(1);
-typeof dehydrateVData(vNum) === 'number'; // true
-dehydrateVData(vNum) === 1; // true
-dehydrateVData('string') === 'string'; // true
-```
-
-## View display
-
-By default, `toString` will be called implicitly when the virtual data is displayed in the view, but sometimes you will encounter problems with display confusion, and rendering an object in **react** will report the following error:
-
-```
-Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
-```
-
-Therefore, it is recommended to use `dehydrateVData` to convert to raw data for display.
-
-## virtual data helper functions
-
-### dehydrateVData
-
-Dehydrate virtual data and return its original value, if target is non-virtual data, return it as it is.
-
-```typescript
-// type
-function dehydrateVData(target: any): any;
-
-// example
-dehydrateVData(1); // 1
-const virtualData = createVirtualData(1);
-dehydrateVData(virtualData); // 1
-```
-
-### stringifyVData
-
-Stringify virtual data, return virtual data id, when returnOriginalIfNotVData is set to false, non-virtual data will be returned as-is.
-
-```typescript
-// type
-function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any;
-
-// example
-stringifyVData(1); // 1
-stringifyVData(1, false); // undefined
-
-const virtualData = createVirtualData(1);
-stringifyVData(virtualData); // [vd:xxxxxx]
-```
-
-### equals
-
-Judge whether two values are equal in a way that is compatible with virtual data. When there is no virtual data to participate in the comparison, it will be strictly compared. Otherwise, it will be compared whether the virtual data id is the same. If there may be virtual data involved in the comparison data, it is recommended to use this function for comparison.
-
-```typescript
-// type
-function equals(prevValue: any, nextValue: any): boolean;
-
-// example
-equals('a', 'a'); // true
-const virtualData1 = createVirtualData(1);
-const virtualData2 = virtualData1.clone(); // clone virtual data
-equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same
-equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx]
-```
-
-###isVData
-
-Determine whether the target data is virtual data
-
-```typescript
-// type
-function isVData(target: any): boolean;
-
-// example
-isVData(1); // false
-const virtualData = createVirtualData(1);
-isVData(virtualData); // true
-isVData('[vd:xxxxxx]'); //true
-```
-
-## Replacement restrictions for virtual data
-
-The tracing mechanism of virtual data can only deeply traverse relevant data, and then replace the data with virtual data identifiers with actual data. If some data is generated by virtual data, it will not be recalculated after virtual data is replaced with actual data.
-
-In the following cases, even if the virtualId is replaced with actual data, the id of the request will not be recalculated. Therefore, if replacement is required, the virtualId must be directly used as a request parameter. The example is as follows:
-
-```javascript
-const deleteTodo = virtualId => {
- return alova.Delete('/deleteTodo', {
- id: dehydrateVData(virtualId) === null ? 1 : 2
- });
-};
-```
-
-But if virtual data is concatenated as string, it will be automatically converted to virtual data id to participate in string concatenation, which will work. In the following cases, the value of the request id at the beginning is `id_[vd:xxxxxx]`, and when virtualId is replaced with the response value (assuming it is replaced with 1), it will be automatically updated to `id_1`.
-
-```javascript
-const deleteTodo = virtualId => {
- return alova.Delete('/deleteTodo', {
- id: 'id'_virtualId
- });
-};
-```
-
-## Next
-
-Although it is simple enough to realize non-inductive interaction, there are still some additional processing compared with conservative requests. The specific implementation is roughly divided into the following steps.
-
-1. Implement functions in a conservative request manner;
-2. Manually update the list data to realize localized data compensation;
-3. Record data operations so that you can manually add to the latest records when there are unsubmitted modifications;
-4. Manually compensate the unsubmitted data to the list, so that the latest status can be displayed even if the data is not submitted;
-5. When modifying unsubmitted data, intercept requests with virtual data;
-
-Next, we will demonstrate with a simple example.
+---
+title: Virtual data
+---
+
+In fact, virtual data is a reference object with a unique id, and its tracking mechanism is realized by first generating a mapping between virtual data id and response data, and then finding and replacing it with the actual value through virtual data id.
+
+When the original value is a reference type, the performance is the same as the original value, but the virtual data of the basic type uses `Number, String, Boolean` encapsulation classes, as well as custom `Undefined, Null` encapsulation classes, and their expressions are the same as There are some deviations from the original value. The characteristics of the virtual data and the use of auxiliary functions for the virtual data are listed below. The details of the auxiliary functions will be introduced at the end of the chapter.
+
+## string concatenation
+
+When virtual data is concatenated, it will be converted to virtual data id for splicing.
+
+```javascript
+const virtualData = createVirtualData(1);
+'a' + virtualData; // a[vd:xxxxxx]
+1 + virtualData; // 1[vd:xxxxxx]
+```
+
+## Data Comparison
+
+Virtual data cannot be directly used for comparison, but virtual data and actual data are often mixed and compared in actual scenarios. In this case, `equals` can be used for comparison.
+
+```javascript
+import { equals } from '@alova/scene-*';
+
+equals('a', 'a'); // true
+
+const virtualData1 = createVirtualData(1);
+const virtualData2 = virtualData1.clone(); // clone virtual data
+equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same
+equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx]
+```
+
+## Participate in operations
+
+When participating in operations such as `+-*/%`, numerical comparison, and bit operations, it cannot be automatically converted to the original value. It can be converted to the original value through `dehydrateVData` and then calculated.
+
+```javascript
+import { dehydrateVData } from '@alova/scene-*';
+
+const virtualData = createVirtualData(1);
+dehydrateVData(virtualData) + 1; // 2
+dehydrateVData(virtualData) > 0; // true
+```
+
+## type operator
+
+Because the virtual data is implemented using the encapsulation class on the basic data type, `object` will always be returned when using `typeof` to get the type, and it can also be converted to the original value by `dehydrateVData` to get the type
+
+```javascript
+const vNum = createVirtualResponse(1);
+typeof vNum === 'object'; // true
+const vUndef = createVirtualResponse(undefined);
+typeof vUndef === 'object'; // true
+
+typeof dehydrateVData(vNum) === 'number'; // true
+typeof dehydrateVData(vUndef) === 'undefined'; // true
+```
+
+To solve this problem, you can use the virtual data helper function **dehydrateVData**, which can get the original value of a virtual data, and return it unchanged when encountering non-virtual data
+
+```javascript
+const vNum = createVirtualResponse(1);
+typeof dehydrateVData(vNum) === 'number'; // true
+dehydrateVData(vNum) === 1; // true
+dehydrateVData('string') === 'string'; // true
+```
+
+## View display
+
+By default, `toString` will be called implicitly when the virtual data is displayed in the view, but sometimes you will encounter problems with display confusion, and rendering an object in **react** will report the following error:
+
+```
+Uncaught Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
+```
+
+Therefore, it is recommended to use `dehydrateVData` to convert to raw data for display.
+
+## virtual data helper functions
+
+### dehydrateVData
+
+Dehydrate virtual data and return its original value, if target is non-virtual data, return it as it is.
+
+```typescript
+// type
+function dehydrateVData(target: any): any;
+
+// example
+dehydrateVData(1); // 1
+const virtualData = createVirtualData(1);
+dehydrateVData(virtualData); // 1
+```
+
+### stringifyVData
+
+Stringify virtual data, return virtual data id, when returnOriginalIfNotVData is set to false, non-virtual data will be returned as-is.
+
+```typescript
+// type
+function stringifyVData(target: any, returnOriginalIfNotVData?: boolean): any;
+
+// example
+stringifyVData(1); // 1
+stringifyVData(1, false); // undefined
+
+const virtualData = createVirtualData(1);
+stringifyVData(virtualData); // [vd:xxxxxx]
+```
+
+### equals
+
+Judge whether two values are equal in a way that is compatible with virtual data. When there is no virtual data to participate in the comparison, it will be strictly compared. Otherwise, it will be compared whether the virtual data id is the same. If there may be virtual data involved in the comparison data, it is recommended to use this function for comparison.
+
+```typescript
+// type
+function equals(prevValue: any, nextValue: any): boolean;
+
+// example
+equals('a', 'a'); // true
+const virtualData1 = createVirtualData(1);
+const virtualData2 = virtualData1.clone(); // clone virtual data
+equals(virtualData1, virtualData2); // true when the ids of virtualData1 and virtualData2 are the same
+equals(virtualData1, '[vd:xxxxxx]'); // true when the id of virtualData1 is also [vd:xxxxxx]
+```
+
+###isVData
+
+Determine whether the target data is virtual data
+
+```typescript
+// type
+function isVData(target: any): boolean;
+
+// example
+isVData(1); // false
+const virtualData = createVirtualData(1);
+isVData(virtualData); // true
+isVData('[vd:xxxxxx]'); //true
+```
+
+## Replacement restrictions for virtual data
+
+The tracing mechanism of virtual data can only deeply traverse relevant data, and then replace the data with virtual data identifiers with actual data. If some data is generated by virtual data, it will not be recalculated after virtual data is replaced with actual data.
+
+In the following cases, even if the virtualId is replaced with actual data, the id of the request will not be recalculated. Therefore, if replacement is required, the virtualId must be directly used as a request parameter. The example is as follows:
+
+```javascript
+const deleteTodo = virtualId => {
+ return alova.Delete('/deleteTodo', {
+ id: dehydrateVData(virtualId) === null ? 1 : 2
+ });
+};
+```
+
+But if virtual data is concatenated as string, it will be automatically converted to virtual data id to participate in string concatenation, which will work. In the following cases, the value of the request id at the beginning is `id_[vd:xxxxxx]`, and when virtualId is replaced with the response value (assuming it is replaced with 1), it will be automatically updated to `id_1`.
+
+```javascript
+const deleteTodo = virtualId => {
+ return alova.Delete('/deleteTodo', {
+ id: 'id'_virtualId
+ });
+};
+```
+
+## Next
+
+Although it is simple enough to realize non-inductive interaction, there are still some additional processing compared with conservative requests. The specific implementation is roughly divided into the following steps.
+
+1. Implement functions in a conservative request manner;
+2. Manually update the list data to realize localized data compensation;
+3. Record data operations so that you can manually add to the latest records when there are unsubmitted modifications;
+4. Manually compensate the unsubmitted data to the list, so that the latest status can be displayed even if the data is not submitted;
+5. When modifying unsubmitted data, intercept requests with virtual data;
+
+Next, we will demonstrate with a simple example.
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md
similarity index 96%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md
index 0e7c87be9..7d066c062 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/03-start-silent-factory.md
@@ -1,107 +1,106 @@
----
-title: Boot silent factory
-sidebar_position: 30
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-We put all scene request strategies in the js package called `@alova/scene-*`, you need to install it before using it.
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-The silent queue is not started by default, and we need to specify the startup parameters for initialization. In general, call `bootSilentFactory` in the entry file to initialize the silent factory, which will read unexecuted requests to the corresponding silent through the specified configuration items queues and start those queues.
-
-
-
-
-```javascript
-import { bootSilentFactory } from '@alova/scene-vue';
-import { alovaInst } from '@/api';
-
-bootSilentFactory({
- // Specify the alova instance at startup to request information storage and request sending
- alova: alovaInst,
-
- // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
- delay: 1000
-});
-```
-
-
-
-
-
-```javascript
-import { bootSilentFactory } from '@alova/scene-react';
-import { alovaInst } from '@/api';
-
-bootSilentFactory({
- // Specify the alova instance at startup to request information storage and request sending
- alova: alovaInst,
-
- // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
- delay: 1000
-});
-```
-
-
-
-
-
-```javascript
-import { bootSilentFactory } from '@alova/scene-svelte';
-import { alovaInst } from '@/api';
-
-bootSilentFactory({
- // Specify the alova instance at startup to request information storage and request sending
- alova: alovaInst,
-
- // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
- delay: 1000
-});
-```
-
-
-
-
-:::warning `delay` parameter description
-
-In actual scenarios, when entering the current page, a request is also sent to load the page data. In order to ensure that the user can see the page data faster, the request to load the data needs to be forwarded to the beginning of the queue, otherwise it may cause the loading data to fail. The request is placed at the end of the queue. At this time, it is necessary to wait until all the previous requests are completed before loading the page data. This is obviously inappropriate. Therefore, by delaying initialization for a period of time, the request for loading data enters the queue first to achieve "queue jumping" effect, the specific delay time depends on the time required for page rendering.
-
-:::
+---
+title: Boot silent factory
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+We put all scene request strategies in the js package called `@alova/scene-*`, you need to install it before using it.
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+The silent queue is not started by default, and we need to specify the startup parameters for initialization. In general, call `bootSilentFactory` in the entry file to initialize the silent factory, which will read unexecuted requests to the corresponding silent through the specified configuration items queues and start those queues.
+
+
+
+
+```javascript
+import { bootSilentFactory } from '@alova/scene-vue';
+import { alovaInst } from '@/api';
+
+bootSilentFactory({
+ // Specify the alova instance at startup to request information storage and request sending
+ alova: alovaInst,
+
+ // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
+ delay: 1000
+});
+```
+
+
+
+
+
+```javascript
+import { bootSilentFactory } from '@alova/scene-react';
+import { alovaInst } from '@/api';
+
+bootSilentFactory({
+ // Specify the alova instance at startup to request information storage and request sending
+ alova: alovaInst,
+
+ // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
+ delay: 1000
+});
+```
+
+
+
+
+
+```javascript
+import { bootSilentFactory } from '@alova/scene-svelte';
+import { alovaInst } from '@/api';
+
+bootSilentFactory({
+ // Specify the alova instance at startup to request information storage and request sending
+ alova: alovaInst,
+
+ // Delay start time, in milliseconds, the default is 2000ms, see the follow-up instructions for details
+ delay: 1000
+});
+```
+
+
+
+
+:::warning `delay` parameter description
+
+In actual scenarios, when entering the current page, a request is also sent to load the page data. In order to ensure that the user can see the page data faster, the request to load the data needs to be forwarded to the beginning of the queue, otherwise it may cause the loading data to fail. The request is placed at the end of the queue. At this time, it is necessary to wait until all the previous requests are completed before loading the page data. This is obviously inappropriate. Therefore, by delaying initialization for a period of time, the request for loading data enters the queue first to achieve "queue jumping" effect, the specific delay time depends on the time required for page rendering.
+
+:::
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md
similarity index 95%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md
index a57c1774f..b6c811cf1 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/04-conservative-request.md
@@ -1,136 +1,135 @@
----
-title: Step 1 - Implement features with conservative requests
-sidebar_position: 40
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-Take Todo management as an example to realize the creation, editing, deletion and other functions of Todo in the non-sense interaction mode, and the key code related to the request will be provided in the following chapters.
-
-> The [simple list page example](/tutorial/example/silent-submit-simple-list) here contains the complete code, you can enter the experience.
-
-In the `@alova/scene-*` js package, **useSQRequest** will be used to replace **useRequest** provided by alova, and then the most common conservative request mode will be implemented first, and then the process will be done step by step Interactive mode compatibility.
-
-## Create an alova instance and related methods
-
-```javascript title="api.js"
-import { createAlova } from 'alova';
-
-export const alovaInst = createAlova({
- /*...*/
-});
-
-/** Load todo list */
-const todoList = () => alovaInst.Get('/todo');
-
-/** Load todo details */
-const todoDetail = id =>
- alovaInst.Get('/todo', {
- params: { id }
- });
-
-/** Create and edit todo items */
-const createOrEditTodo = (data, id) =>
- alovaInst.Post('/todo', {
- data,
- id
- });
-
-/** Delete the todo item */
-const deleteTodo = id => alovaInst.Delete('/todo', { id });
-```
-
-## Start the silent factory
-
-```javascript title="main.js"
-import { bootSilentFactory } from '@alova/scene-*';
-import { alovaInst } from './api.js';
-
-bootSilentFactory({
- alova: alovaInst
-});
-```
-
-## Load the Todo list
-
-Load and display page data in the simplest way
-
-```javascript
-import { useSQRequest } from '@alova/scene-vue';
-import { todoList } from './api.js';
-const { data, loading, error } = useSQRequest(todoList, {
- initialData: []
-});
-```
-
-## Enter the Todo creation/editing page
-
-When creating a todo item, the id is empty, and no request for obtaining details is sent. When editing a todo item, if the id has a value, the detailed data will be obtained.
-
-```javascript
-import { useSQRequest } from '@alova/scene-*';
-import { todoDetail } from './api.js';
-
-const id = /* todo id */;
-const { loading, data } = useSQRequest(() => todoDetail(id), {
- initialData: {
- title: '',
- time: new Date()
- },
- immediate: !!id
-});
-```
-
-## Create/Edit Todo Items
-
-By submitting an event trigger request, after the submission is successful, call fetch to re-fetch the latest list data, and the interface will automatically display the latest data.
-
-```javascript
-import { useFetcher } from 'alova';
-import { useSQRequest } from '@alova/scene-*';
-import { createOrEditTodo, todoList } from './api.js';
-
-const id = /* todo id */;
-const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, {
- immediate: false,
-});
-
-const { fetch } = useFetcher();
-onSuccess(() => {
- // Re-fetch list data
- fetch(todoList);
-})
-
-// submit event callback function
-const handleSubmit = newData => {
- send(newData, id);
-};
-
-```
-
-## Delete Todo item
-
-Delete the corresponding todo item by id
-
-```javascript
-import { useSQRequest } from '@alova/scene-*';
-import { deleteTodo, todoList } from './api.js';
-
-const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, {
- immediate: false
-});
-
-const { fetch } = useFetcher();
-onSuccess(() => {
- // Re-fetch list data
- fetch(todoList);
-});
-
-// Event callback triggers delete request
-const handleDelete = deletingId => {
- send(deletingId);
-};
-```
-
-So far, a simple Todo list management related request function has been completed, and then we will start to transform it to be compatible with the non-sense interaction mode.
+---
+title: Step 1 - Implement features with conservative requests
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Take Todo management as an example to realize the creation, editing, deletion and other functions of Todo in the non-sense interaction mode, and the key code related to the request will be provided in the following chapters.
+
+> The [simple list page example](/tutorial/example/silent-submit-simple-list) here contains the complete code, you can enter the experience.
+
+In the `@alova/scene-*` js package, **useSQRequest** will be used to replace **useRequest** provided by alova, and then the most common conservative request mode will be implemented first, and then the process will be done step by step Interactive mode compatibility.
+
+## Create an alova instance and related methods
+
+```javascript title="api.js"
+import { createAlova } from 'alova';
+
+export const alovaInst = createAlova({
+ /*...*/
+});
+
+/** Load todo list */
+const todoList = () => alovaInst.Get('/todo');
+
+/** Load todo details */
+const todoDetail = id =>
+ alovaInst.Get('/todo', {
+ params: { id }
+ });
+
+/** Create and edit todo items */
+const createOrEditTodo = (data, id) =>
+ alovaInst.Post('/todo', {
+ data,
+ id
+ });
+
+/** Delete the todo item */
+const deleteTodo = id => alovaInst.Delete('/todo', { id });
+```
+
+## Start the silent factory
+
+```javascript title="main.js"
+import { bootSilentFactory } from '@alova/scene-*';
+import { alovaInst } from './api.js';
+
+bootSilentFactory({
+ alova: alovaInst
+});
+```
+
+## Load the Todo list
+
+Load and display page data in the simplest way
+
+```javascript
+import { useSQRequest } from '@alova/scene-vue';
+import { todoList } from './api.js';
+const { data, loading, error } = useSQRequest(todoList, {
+ initialData: []
+});
+```
+
+## Enter the Todo creation/editing page
+
+When creating a todo item, the id is empty, and no request for obtaining details is sent. When editing a todo item, if the id has a value, the detailed data will be obtained.
+
+```javascript
+import { useSQRequest } from '@alova/scene-*';
+import { todoDetail } from './api.js';
+
+const id = /* todo id */;
+const { loading, data } = useSQRequest(() => todoDetail(id), {
+ initialData: {
+ title: '',
+ time: new Date()
+ },
+ immediate: !!id
+});
+```
+
+## Create/Edit Todo Items
+
+By submitting an event trigger request, after the submission is successful, call fetch to re-fetch the latest list data, and the interface will automatically display the latest data.
+
+```javascript
+import { useFetcher } from 'alova';
+import { useSQRequest } from '@alova/scene-*';
+import { createOrEditTodo, todoList } from './api.js';
+
+const id = /* todo id */;
+const { loading, data, send, onSuccess } = useSQRequest(createOrEditTodo, {
+ immediate: false,
+});
+
+const { fetch } = useFetcher();
+onSuccess(() => {
+ // Re-fetch list data
+ fetch(todoList);
+})
+
+// submit event callback function
+const handleSubmit = newData => {
+ send(newData, id);
+};
+
+```
+
+## Delete Todo item
+
+Delete the corresponding todo item by id
+
+```javascript
+import { useSQRequest } from '@alova/scene-*';
+import { deleteTodo, todoList } from './api.js';
+
+const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, {
+ immediate: false
+});
+
+const { fetch } = useFetcher();
+onSuccess(() => {
+ // Re-fetch list data
+ fetch(todoList);
+});
+
+// Event callback triggers delete request
+const handleDelete = deletingId => {
+ send(deletingId);
+};
+```
+
+So far, a simple Todo list management related request function has been completed, and then we will start to transform it to be compatible with the non-sense interaction mode.
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md
similarity index 95%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md
index 9b616b930..8acba3b9a 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/05-modify-response.md
@@ -1,258 +1,259 @@
----
-title: Step 2 - Adjust Response Handling
-sidebar_position: 50
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-In the conservative request example in the previous section, we called `fetch` to re-fetch the refreshed page after creating, editing, and deleting requests for Todo items. In order to display the results immediately after the operation, we need to make the following adjustments:
-
-1. Set the behavior mode of create, edit and delete requests to `silent`, they will trigger the success callback immediately when the request is made;
-2. Manually update the list, instead of pulling data, use virtual data to occupy the response data of the server;
-3. Save operation records for data compensation when refreshing the page;
-
-## Set behavior mode
-
-Set by configuring the parameter `behavior`, the optional parameters are `queue`, `silent`, `static`, or a function that returns behavior data to dynamically set the behavior mode, the default is `queue`.
-
-The following sets the behavior parameters statically.
-
-```javascript
-useSQRequest(createOrEditTodo, {
- // highlight-start
- behavior: 'silent',
- // highlight-end
- immediate: false
-});
-```
-
-The following is to dynamically set the behavior parameter.
-
-```javascript
-const { send } = useSQRequest(createOrEditTodo, {
- // highlight-start
- // The arg parameter can be passed in through the send function
- behavior: arg => {
- if (arg === 0) return 'silent';
- return 'queue';
- },
- // highlight-end
- immediate: false
-});
-```
-
-> When behavior is set as a function, it will be called every time a request is initiated to determine which behavior to process this request.
-
-## Silent queue description
-
-After setting the behavior parameter to `queue` or `silent`, the request will enter the silent queue and wait for the request to be initiated. By default, they will enter the queue named `default`. You can also specify other queues to save silentMethod instances. without interfering with each other.
-
-```javascript
-useSQRequest(createOrEditTodo, {
- // highlight-start
- // The specified request information enters the queue named queue-2
- queue: 'queue-2',
- // highlight-end
- behavior: 'silent',
- immediate: false
-});
-```
-
-## Manually update the list in the callback
-
-### Update the list after adding/editing
-
-
-
-
-When the list page is not destroyed, such as using the modal box operation on the current page, or using `` (Vue) to keep the page components, the data will still exist. At this time, we use **updateStateEffect** to Update the list data. Compared with the **updateState** exported by alova, it has the function of tracking virtual data. When the response data is obtained, it will automatically track the virtual data in the list data and replace it with the actual data.
-
-```javascript
-import { useSQRequest, updateStateEffect } from '@alova/scene-*';
-import { createOrEditTodo, todoList } from './api.js';
-
-const { onSuccess } = useSQRequest(createOrEditTodo, {
- behavior: 'silent',
- immediate: false,
-
- // highlight-start
- // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data
- // For example, when creating a Todo item, the id of this piece of data will be returned.
- silentDefaultResponse: () => {
- return {
- id: '--'
- };
- }
- // highlight-end
-});
-
-// highlight-start
-onSuccess(({ data, silentMethod }) => {
- // Construct list data items
- const editingItem = {
- ...detail,
-
- // When editing, use the original id, otherwise use the id in the response data
- // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value
- id: id || data.id
- };
-
- // use updateStateEffect instead of updateState
- updateStateEffect(todoList(), todoListRaw => {
- if (id) {
- todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item));
- } else {
- todoListRaw.unshift(editingItem);
- }
- return todoListRaw;
- });
-});
-// highlight-end
-```
-
-> updateStateEffect is used in the same way as [updateState](/tutorial/advanced/update-across-components)
-
-
-
-
-When the list page has been destroyed and the data has been released, such as jumping to a new page, use **setCache** to update the cache data. When the list page is returned, the request will be re-initiated and the updated cache will be hit.
-
-```javascript
-import { useSQRequest, setCache, equals } from '@alova/scene-*';
-import { createOrEditTodo, todoList } from './api.js';
-
-const urlParams = new URLSearchParams(window.location.search);
-const id = urlParams.get('id') || '';
-const { onSuccess } = useSQRequest(createOrEditTodo, {
- behavior: 'silent',
- immediate: false,
-
- // highlight-start
- // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data
- // For example, when creating a Todo item, the id of this piece of data will be returned.
- silentDefaultResponse: () => {
- return {
- id: '--'
- };
- }
- // highlight-end
-});
-// highlight-start
-onSuccess(({ data, silentMethod }) => {
- // Construct list data items
- const editingItem = {
- ...detail,
-
- // When editing, use the original id, otherwise use the id in the response data
- // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value
- id: id || data.id
- };
-
- const method TodoList = todoList();
- setCache(methodTodoList, todoListRaw => {
- if (id) {
- todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item));
- } else {
- todoListRaw.unshift(editingItem);
- }
- return todoListRaw;
- });
- // Call setUpdateState to set response data tracking, so as to achieve the same delayed update effect as updateStateEffect
- if (silentMethod) {
- silentMethod.setUpdateState(methodTodoList);
- silentMethod.save();
- }
-});
-// highlight-end
-```
-
-
-
-
-### Update list after removal
-
-```javascript
-import { useSQRequest, updateStateEffect } from '@alova/scene-*';
-import { deleteTodo, todoList } from './api.js';
-
-const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, {
- immediate: false,
- // highlight-start
- behavior: 'silent'
- // highlight-end
-});
-
-onSuccess(({ sendArgs: [deletingId] }) => {
- updateStateEffect(todoList(), todoListRaw => todoListRaw.filter(item => item.id !== deletingId));
-});
-
-// Event callback triggers delete request
-const handleDelete = deletingId => {
- send(deletingId);
-};
-```
-
-## Save the operation record
-
-It is not enough to just update the list manually. We also need to consider that when the network is restored and there are still waiting requests in the request queue, the list data loaded at this time does not include the part of the unsubmitted request, which will cause certain problems for the user. Puzzled:
-
-> "I have clearly added multiple pieces of data, why is it not in the list?"
-
-Therefore, we need to record the operation and related data in the success callback, so that when the list data is loaded again, the uncommitted data will be manually compensated to the list, so that the list data will always be kept up-to-date.
-
-Saving operation records is also very simple, you only need to mount the relevant data to the silentMethod instance, and it will be persisted along with the instance.
-
-### create/edit success callback
-
-```javascript
-//...
-onSuccess(({ silentMethod }) => {
- // Construct list data items
- const editingItem = {
- ...detail,
- id: id || data.id
- };
- //...
- // highlight-start
- if (silentMethod) {
- // Set the name for subsequent queries
- // If editingItem.id is virtual data will be automatically converted to its id
- silentMethod.entity.setName('edit' + editingItem.id);
- silentMethod.reviewData = {
- operate: id ? 'edit' : 'add',
- data: editingItem
- };
- silentMethod.save();
- }
- // highlight-end
-});
-```
-
-### delete success callback
-
-```javascript
-//...
-onSuccess(({ sendArgs: [deletingId], silentMethod }) => {
- //...
- // highlight-start
- if (silentMethod) {
- silentMethod.reviewData = {
- operate: 'delete',
- data: {
- id: deletingId
- }
- };
- silentMethod.save();
- }
- // highlight-end
-});
-```
-
-### Precautions
-
-1. In the onSuccess callback function, silentMethod has a value only in the `queue` and `silent` behavior modes;
-2. Generally speaking, you can use `silentMethod.a = ...` or `silentMethod.b = ...` to save operation records, but it will report an error in typescript, so _reviewData_ is specially provided as a silent Submit the save attribute of the operation record;
-3. After modifying the silentMethod data, you need to save the modification through `silentMethod.save()`;
-
-The next step is to set retry parameters on silent submit requests.
+---
+title: Step 2 - Adjust Response Handling
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In the conservative request example in the previous section, we called `fetch` to re-fetch the refreshed page after creating, editing, and deleting requests for Todo items. In order to display the results immediately after the operation, we need to make the following adjustments:
+
+1. Set the behavior mode of create, edit and delete requests to `silent`, they will trigger the success callback immediately when the request is made;
+2. Manually update the list, instead of pulling data, use virtual data to occupy the response data of the server;
+3. Save operation records for data compensation when refreshing the page;
+
+## Set behavior mode
+
+Set by configuring the parameter `behavior`, the optional parameters are `queue`, `silent`, `static`, or a function that returns behavior data to dynamically set the behavior mode, the default is `queue`.
+
+The following sets the behavior parameters statically.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ // highlight-start
+ behavior: 'silent',
+ // highlight-end
+ immediate: false
+});
+```
+
+The following is to dynamically set the behavior parameter.
+
+```javascript
+const { send } = useSQRequest(createOrEditTodo, {
+ // highlight-start
+ // The arg parameter can be passed in through the send function
+ behavior: arg => {
+ if (arg === 0) return 'silent';
+ return 'queue';
+ },
+ // highlight-end
+ immediate: false
+});
+```
+
+> When behavior is set as a function, it will be called every time a request is initiated to determine which behavior to process this request.
+
+## Silent queue description
+
+After setting the behavior parameter to `queue` or `silent`, the request will enter the silent queue and wait for the request to be initiated. By default, they will enter the queue named `default`. You can also specify other queues to save silentMethod instances. without interfering with each other.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ // highlight-start
+ // The specified request information enters the queue named queue-2
+ queue: 'queue-2',
+ // highlight-end
+ behavior: 'silent',
+ immediate: false
+});
+```
+
+## Manually update the list in the callback
+
+### Update the list after adding/editing
+
+
+
+
+When the list page is not destroyed, such as using the modal box operation on the current page, or using `` (Vue) to keep the page components, the data will still exist. At this time, we use **updateStateEffect** to Update the list data. Compared with the **updateState** exported by alova, it has the function of tracking virtual data. When the response data is obtained, it will automatically track the virtual data in the list data and replace it with the actual data.
+
+```javascript
+import { useSQRequest, updateStateEffect } from '@alova/scene-*';
+import { createOrEditTodo, todoList } from './api.js';
+
+const { onSuccess } = useSQRequest(createOrEditTodo, {
+ behavior: 'silent',
+ immediate: false,
+
+ // highlight-start
+ // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data
+ // For example, when creating a Todo item, the id of this piece of data will be returned.
+ silentDefaultResponse: () => {
+ return {
+ id: '--'
+ };
+ }
+ // highlight-end
+});
+
+// highlight-start
+onSuccess(({ data, silentMethod }) => {
+ // Construct list data items
+ const editingItem = {
+ ...detail,
+
+ // When editing, use the original id, otherwise use the id in the response data
+ // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value
+ id: id || data.id
+ };
+
+ // use updateStateEffect instead of updateState
+ updateStateEffect(todoList(), todoListRaw => {
+ if (id) {
+ todoListRaw = todoListRaw.map(item => (item.id === id ? editingItem : item));
+ } else {
+ todoListRaw.unshift(editingItem);
+ }
+ return todoListRaw;
+ });
+});
+// highlight-end
+```
+
+> updateStateEffect is used in the same way as [updateState](/tutorial/advanced/update-across-components)
+
+
+
+
+When the list page has been destroyed and the data has been released, such as jumping to a new page, use **setCache** to update the cache data. When the list page is returned, the request will be re-initiated and the updated cache will be hit.
+
+```javascript
+import { useSQRequest, setCache, equals } from '@alova/scene-*';
+import { createOrEditTodo, todoList } from './api.js';
+
+const urlParams = new URLSearchParams(window.location.search);
+const id = urlParams.get('id') || '';
+const { onSuccess } = useSQRequest(createOrEditTodo, {
+ behavior: 'silent',
+ immediate: false,
+
+ // highlight-start
+ // Before processing list updates, it is necessary to construct virtual response data of the same structure according to the structure of the response data
+ // For example, when creating a Todo item, the id of this piece of data will be returned.
+ silentDefaultResponse: () => {
+ return {
+ id: '--'
+ };
+ }
+ // highlight-end
+});
+// highlight-start
+onSuccess(({ data, silentMethod }) => {
+ // Construct list data items
+ const editingItem = {
+ ...detail,
+
+ // When editing, use the original id, otherwise use the id in the response data
+ // When submitting silently, data.id is virtual data, and when in static behavior mode, data.id is the actual id value
+ id: id || data.id
+ };
+
+ const method TodoList = todoList();
+ setCache(methodTodoList, todoListRaw => {
+ if (id) {
+ todoListRaw = todoListRaw.map(item => (equals(item.id, id) ? editingItem : item));
+ } else {
+ todoListRaw.unshift(editingItem);
+ }
+ return todoListRaw;
+ });
+ // Call setUpdateState to set response data tracking, so as to achieve the same delayed update effect as updateStateEffect
+ if (silentMethod) {
+ silentMethod.setUpdateState(methodTodoList);
+ silentMethod.save();
+ }
+});
+// highlight-end
+```
+
+
+
+
+### Update list after removal
+
+```javascript
+import { useSQRequest, updateStateEffect } from '@alova/scene-*';
+import { deleteTodo, todoList } from './api.js';
+
+const { loading, data, send, onSuccess } = useSQRequest(deleteTodo, {
+ immediate: false,
+ // highlight-start
+ behavior: 'silent'
+ // highlight-end
+});
+
+onSuccess(({ sendArgs: [deletingId] }) => {
+ updateStateEffect(todoList(), todoListRaw =>
+ todoListRaw.filter(item => item.id !== deletingId)
+ );
+});
+
+// Event callback triggers delete request
+const handleDelete = deletingId => {
+ send(deletingId);
+};
+```
+
+## Save the operation record
+
+It is not enough to just update the list manually. We also need to consider that when the network is restored and there are still waiting requests in the request queue, the list data loaded at this time does not include the part of the unsubmitted request, which will cause certain problems for the user. Puzzled:
+
+> "I have clearly added multiple pieces of data, why is it not in the list?"
+
+Therefore, we need to record the operation and related data in the success callback, so that when the list data is loaded again, the uncommitted data will be manually compensated to the list, so that the list data will always be kept up-to-date.
+
+Saving operation records is also very simple, you only need to mount the relevant data to the silentMethod instance, and it will be persisted along with the instance.
+
+### create/edit success callback
+
+```javascript
+//...
+onSuccess(({ silentMethod }) => {
+ // Construct list data items
+ const editingItem = {
+ ...detail,
+ id: id || data.id
+ };
+ //...
+ // highlight-start
+ if (silentMethod) {
+ // Set the name for subsequent queries
+ // If editingItem.id is virtual data will be automatically converted to its id
+ silentMethod.entity.setName('edit' + editingItem.id);
+ silentMethod.reviewData = {
+ operate: id ? 'edit' : 'add',
+ data: editingItem
+ };
+ silentMethod.save();
+ }
+ // highlight-end
+});
+```
+
+### delete success callback
+
+```javascript
+//...
+onSuccess(({ sendArgs: [deletingId], silentMethod }) => {
+ //...
+ // highlight-start
+ if (silentMethod) {
+ silentMethod.reviewData = {
+ operate: 'delete',
+ data: {
+ id: deletingId
+ }
+ };
+ silentMethod.save();
+ }
+ // highlight-end
+});
+```
+
+### Precautions
+
+1. In the onSuccess callback function, silentMethod has a value only in the `queue` and `silent` behavior modes;
+2. Generally speaking, you can use `silentMethod.a = ...` or `silentMethod.b = ...` to save operation records, but it will report an error in typescript, so _reviewData_ is specially provided as a silent Submit the save attribute of the operation record;
+3. After modifying the silentMethod data, you need to save the modification through `silentMethod.save()`;
+
+The next step is to set retry parameters on silent submit requests.
diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md
new file mode 100644
index 000000000..b9685ed3a
--- /dev/null
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/06-request-retry.md
@@ -0,0 +1,143 @@
+---
+title: Step 3 - Set Request Retry
+---
+
+When a request enters the silent queue, you can set request retry parameters for it to ensure its request success rate. This is valid when the behavior mode is set to **queue** and **silent**. The difference is, The request under the behavior of **silent** is persistent by default, and the request will continue to be sent even if it is refreshed before the request succeeds, while the request under the behavior of **queue** will not be persisted and will be cleared after refreshing.
+
+## Maximum number of retries
+
+Set the maximum number of retries, no retries by default.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ // highlight-start
+ // The number of retries is 3 times
+ maxRetryTimes: 3
+ // highlight-end
+});
+```
+
+## Request delay time
+
+By default, each retry interval is 1000ms, and we can customize the delay time of each retry in the avoidance strategy.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ maxRetryTimes: 3,
+ // highlight-start
+ // Requests are delayed by 2000ms each time
+ backoff: {
+ delay: 2000
+ }
+ // highlight-end
+});
+```
+
+If you need to increase the delay time according to the rules, you can set a growth factor for it.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ maxRetryTimes: 3,
+ backoff: {
+ delay: 2000,
+ // highlight-start
+ // When multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, and the third is 6 seconds
+ multiplier: 2
+ // highlight-end
+ }
+});
+```
+
+not enough? You can even add a random jitter value to each delay to make it look less regular
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ maxRetryTimes: 3,
+ backoff: {
+ delay: 2000,
+ multiplier: 2,
+ // highlight-start
+ /**
+ * The initial jitter percentage value of the delay request, the range is 0-1
+ * When only startQuiver is set, endQuiver defaults to 1
+ * For example set to 0.5, it will add 50% to 100% random time on the current delay time
+ * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver
+ */
+ startQuiver: 0.5,
+
+ /**
+ * The jitter end percentage value of the delayed request, the range is 0-1
+ * When only endQuiver is set, startQuiver defaults to 0
+ * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time
+ * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver
+ */
+ endQuiver: 0.8;
+ // highlight-end
+ }
+});
+```
+
+## set retry rules
+
+By default, as long as the request fails, it will be retried. The request failure is divided into the following situations:
+
+1. The request is wrong, and the error is not caught by the global `onError` hook;
+2. The request was successful, but an error was thrown in the global `onSuccess` hook;
+
+But in reality, not all requests need to be retried. For example, when a server error occurs or the network is disconnected, it should not be retried. In this case, it is necessary to set a retry judgment rule. When a request fails, an instance of `Error` is usually obtained. We can set a regular expression to match `error.message` or `error.name`, and if the match passes, no retry will be made.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ // highlight-start
+ // When the thrown error name is 500, or the wrong message matches network error, do not retry
+ retryError: {
+ name: /^500$/,
+ message: /network error/i
+ }
+ // highlight-end
+});
+```
+
+You can also set one of the matching rules. When only setting the matching rules for `message`, it can be directly abbreviated as a regular expression.
+
+```javascript
+// Only set the name that matches the error
+useSQRequest(createOrEditTodo, {
+ //...
+ retryError: {
+ name: /^500$/
+ }
+});
+
+// Only set the message that matches the error
+useSQRequest(createOrEditTodo, {
+ //...
+ retryError: /network error/i
+});
+```
+
+In order not to pollute the error message, usually we will put the error code returned by the server in `error.name`, of course, you can also splice it into `error.message`, the error handling example of Response is as follows:
+
+```javascript
+const alovaInst = createAlova({
+ //...
+ responded: {
+ onSuccess(response) {
+ // Error thrown on 500 error
+ if (response.status === 500) {
+ const error = new Error(response.statusText);
+ error.name = response.status;
+ throw error;
+ }
+ return response.json();
+ }
+ }
+});
+```
+
+In the next step, the saved operation records will be used to perform data compensation on the list data to achieve the latest state.
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md
similarity index 97%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md
index 48dcc797e..d526dd268 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/07-data-compensation.md
@@ -1,56 +1,55 @@
----
-title: Step 4 - Data Compensation
-sidebar_position: 70
----
-
-The user may perform some data operations in a disconnected environment. At this time, the silent queue will be full of unsubmitted requests. When the network is restored, due to the limitation of the timing mechanism, it will take a little time to complete these requests. The list loaded at this time The data does not include unsubmitted requests, which can cause some confusion for users:
-
-> "I have clearly added multiple pieces of data, why is it not in the list?"
-
-Therefore, we need to manually compensate the unsubmitted data to the list, so that the list data is always kept up-to-date. In this step, the saved operation records will be used to compensate the list data. It is actually very simple. We It is only necessary to traverse the silentMethod instance of the relevant queue after the list request is successful, and update the operation records recorded in the previous step to the list data.
-
-```javascript
-import { useSQRequest, filterSilentMethods, equals } from '@alova/scene-vue';
-import { todoList } from './api.js';
-const { data, loading, onSuccess } = useSQRequest(todoList, {
- initialData: []
-});
-
-onSuccess(() => {
- // Get all silentMethod instances under the default queue
- const silentMethods = filterSilentMethods();
- silentMethods.forEach(({ reviewData }) => {
- if (!reviewData) {
- return;
- }
- const { operate, data } = reviewData;
- const index = todoListData.findIndex(({ id }) => equals(id, data.id));
- if ((operate === 'edit' || operate === 'remove') && index >= 0) {
- operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1);
- } else if (operate === 'add' && index < 0) {
- // There will be added uncommitted items when re-requesting and hitting the cache, these need to be filtered
- todoListData.unshift(data);
- }
- });
-});
-```
-
-## Explanation of related functions
-
-### filterSilentMethods
-
-Filter out the specified silentMethod instance by method name or regular expression, which is defined as follows:
-
-```typescript
-function filterSilentMethods(
- methodNameMatcher?: string | number | RegExp,
- queueName?: string,
- filterActive?: boolean
-): SilentMethod[];
-```
-
-**methodNameMatcher: **method name matcher, if it is a number or string, it will filter out the results that completely match the name, if it is a regular expression, it will filter out the matching results, if it is not passed, it will filter out all the results;
-
-**queueName**: Specify the queue to search for, if not uploaded, the _default_ queue will be searched by default;
-
-**filterActive**: whether to filter out the active state instance
+---
+title: Step 4 - Data Compensation
+---
+
+The user may perform some data operations in a disconnected environment. At this time, the silent queue will be full of unsubmitted requests. When the network is restored, due to the limitation of the timing mechanism, it will take a little time to complete these requests. The list loaded at this time The data does not include unsubmitted requests, which can cause some confusion for users:
+
+> "I have clearly added multiple pieces of data, why is it not in the list?"
+
+Therefore, we need to manually compensate the unsubmitted data to the list, so that the list data is always kept up-to-date. In this step, the saved operation records will be used to compensate the list data. It is actually very simple. We It is only necessary to traverse the silentMethod instance of the relevant queue after the list request is successful, and update the operation records recorded in the previous step to the list data.
+
+```javascript
+import { useSQRequest, filterSilentMethods, equals } from '@alova/scene-vue';
+import { todoList } from './api.js';
+const { data, loading, onSuccess } = useSQRequest(todoList, {
+ initialData: []
+});
+
+onSuccess(() => {
+ // Get all silentMethod instances under the default queue
+ const silentMethods = filterSilentMethods();
+ silentMethods.forEach(({ reviewData }) => {
+ if (!reviewData) {
+ return;
+ }
+ const { operate, data } = reviewData;
+ const index = todoListData.findIndex(({ id }) => equals(id, data.id));
+ if ((operate === 'edit' || operate === 'remove') && index >= 0) {
+ operate === 'edit' ? todoListData.splice(index, 1, data) : todoListData.splice(index, 1);
+ } else if (operate === 'add' && index < 0) {
+ // There will be added uncommitted items when re-requesting and hitting the cache, these need to be filtered
+ todoListData.unshift(data);
+ }
+ });
+});
+```
+
+## Explanation of related functions
+
+### filterSilentMethods
+
+Filter out the specified silentMethod instance by method name or regular expression, which is defined as follows:
+
+```typescript
+function filterSilentMethods(
+ methodNameMatcher?: string | number | RegExp,
+ queueName?: string,
+ filterActive?: boolean
+): SilentMethod[];
+```
+
+**methodNameMatcher: **method name matcher, if it is a number or string, it will filter out the results that completely match the name, if it is a regular expression, it will filter out the matching results, if it is not passed, it will filter out all the results;
+
+**queueName**: Specify the queue to search for, if not uploaded, the _default_ queue will be searched by default;
+
+**filterActive**: whether to filter out the active state instance
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md
similarity index 97%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md
index b2dd6c25d..08a361e62 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/08-edit-item.md
@@ -1,111 +1,110 @@
----
-title: Step 5 - Edit Data
-sidebar_position: 80
----
-
-> What should I do when the user needs to edit data when the network is disconnected?
-
-At this point, two situations need to be explained:
-
-1. The list data can meet the data echo of the edit page. At this time, the list data can be passed to the edit page without requesting. At this time, all list data supports editing in the silent submission mode;
-2. The echo data on the edit page needs to be obtained through the api, and only the locally cached list items can echo the data normally, for example:
- 1. For the list items that have been accessed before the network is disconnected, the request can hit the cache again;
- 2. Created through the silent submission mode, but the list item has not been successfully submitted, and the submitted data still exists in the silentMethod instance;
-
-And here we will focus on the case of **2-2**.
-
-## Edit silent submit items
-
-In the previous chapters, we know that when the newly created data item has not been successfully submitted, the virtual data will be used as the placeholder for the id. Usually, we also get the data item through the id. At this time, we are in `useSQRequeset` Virtual data interception is implemented on the above, if a request is accompanied by virtual data information, it will be intercepted before sending and the data can be specified to replace the response data, and the request will be abandoned.
-
-Remember the **silentMethod.reviewData** saved in [Step 2 - Adjust Response Handling](/tutorial/strategy/sensorless-data-interaction/modify-response)?
-
-```javascript
-onSuccess(({ silentMethod }) => {
- // Construct list data items
- const editingItem = {
- ...detail,
- id: id || data.id
- };
- //...
- if (silentMethod) {
- // highlight-start
- // Setting the name is to find the corresponding silentMethod instance when intercepting
- silentMethod.entity.setName('edit' + editingItem.id);
- silentMethod.reviewData = {
- operate: id ? 'edit' : 'add',
- data: editingItem
- };
- // Don't forget to call save
- silentMethod.save();
- // highlight-end
- }
-});
-```
-
-It can be used not only for data compensation, but also for echoing data in edit pages.
-
-```javascript
-const { loading, data } = useSQRequest(id => todoDetail(id), {
- initialData: {
- title: '',
- time: new Date()
- },
- immediate: false,
-
- // highlight-start
- // Set the interception function, the function will be called when there is virtual data in this request
- // If reviewData is returned, it will replace the response data and give up this request, otherwise the request will still be initiated
- vDataCaptured: () => {
- const targetSM = filterSilentMethods('edit' + todoId).pop();
- if (targetSM?.reviewData) {
- return { ...targetSM.reviewData.data };
- }
- }
- // highlight-end
-});
-```
-
-:::warning Caution
-
-You can save enough data in **silentMethod.reviewData** to satisfy both list data compensation and edit page data echo.
-
-:::
-
-So far, data items created through silent submit mode also support editing! What's the problem, um... and one last one.
-
-## When the data item being edited is submitted successfully
-
-When the user is editing a data item that has not been successfully submitted, it suddenly submits successfully! At this time, we need to replace the virtual data used in the edit page with actual data, for example, replace the virtual id with the actual id, and use the actual id to submit in the next edit. This is also very simple, we only need to monitor This is done by silently submitting the success event, which will receive a data collection consisting of virtual data and real data.
-
-```javascript
-import { onSilentSubmitSuccess, stringifyVData } from '@alova/scene-*';
-
-//...
-// id is virtual data during initialization
-let id = /* todo virtual id */;
-
-// highlight-start
-// Binding listener silently submits the successful event to update the id, and returns the unbind function, don't forget to call the unbind function when the component is destroyed
-const unbindEvent = onSilentSubmitSuccess(event => {
- const vDataId = stringifyVData(id);
- if (event.vDataResponse[vDataId]) {
- id = event.vDataResponse[vDataId];
-
- // The following is to change the virtual id in the url to the actual id
- history.replaceState(null, '', '?id=' + currentId);
- }
-});
-// highlight-end
-```
-
-Here, the `event.vDataResponse` value is a collection of virtual data id and actual data, and its format is as follows:
-
-```javascript
-{
- '[vd:aaaaaa]': { id: 1 },
- '[vd:bbbbbb]': 1
-}
-```
-
-So far, we have completed all the content of a simple list of non-inductive interaction, but in other application scenarios such as editing applications, complex list management, etc., we may encounter more different needs. What else does alova have at this time? What are the features we can use? Please read the next chapter!
+---
+title: Step 5 - Edit Data
+---
+
+> What should I do when the user needs to edit data when the network is disconnected?
+
+At this point, two situations need to be explained:
+
+1. The list data can meet the data echo of the edit page. At this time, the list data can be passed to the edit page without requesting. At this time, all list data supports editing in the silent submission mode;
+2. The echo data on the edit page needs to be obtained through the api, and only the locally cached list items can echo the data normally, for example:
+ 1. For the list items that have been accessed before the network is disconnected, the request can hit the cache again;
+ 2. Created through the silent submission mode, but the list item has not been successfully submitted, and the submitted data still exists in the silentMethod instance;
+
+And here we will focus on the case of **2-2**.
+
+## Edit silent submit items
+
+In the previous chapters, we know that when the newly created data item has not been successfully submitted, the virtual data will be used as the placeholder for the id. Usually, we also get the data item through the id. At this time, we are in `useSQRequeset` Virtual data interception is implemented on the above, if a request is accompanied by virtual data information, it will be intercepted before sending and the data can be specified to replace the response data, and the request will be abandoned.
+
+Remember the **silentMethod.reviewData** saved in [Step 2 - Adjust Response Handling](/tutorial/strategy/sensorless-data-interaction/modify-response)?
+
+```javascript
+onSuccess(({ silentMethod }) => {
+ // Construct list data items
+ const editingItem = {
+ ...detail,
+ id: id || data.id
+ };
+ //...
+ if (silentMethod) {
+ // highlight-start
+ // Setting the name is to find the corresponding silentMethod instance when intercepting
+ silentMethod.entity.setName('edit' + editingItem.id);
+ silentMethod.reviewData = {
+ operate: id ? 'edit' : 'add',
+ data: editingItem
+ };
+ // Don't forget to call save
+ silentMethod.save();
+ // highlight-end
+ }
+});
+```
+
+It can be used not only for data compensation, but also for echoing data in edit pages.
+
+```javascript
+const { loading, data } = useSQRequest(id => todoDetail(id), {
+ initialData: {
+ title: '',
+ time: new Date()
+ },
+ immediate: false,
+
+ // highlight-start
+ // Set the interception function, the function will be called when there is virtual data in this request
+ // If reviewData is returned, it will replace the response data and give up this request, otherwise the request will still be initiated
+ vDataCaptured: () => {
+ const targetSM = filterSilentMethods('edit' + todoId).pop();
+ if (targetSM?.reviewData) {
+ return { ...targetSM.reviewData.data };
+ }
+ }
+ // highlight-end
+});
+```
+
+:::warning Caution
+
+You can save enough data in **silentMethod.reviewData** to satisfy both list data compensation and edit page data echo.
+
+:::
+
+So far, data items created through silent submit mode also support editing! What's the problem, um... and one last one.
+
+## When the data item being edited is submitted successfully
+
+When the user is editing a data item that has not been successfully submitted, it suddenly submits successfully! At this time, we need to replace the virtual data used in the edit page with actual data, for example, replace the virtual id with the actual id, and use the actual id to submit in the next edit. This is also very simple, we only need to monitor This is done by silently submitting the success event, which will receive a data collection consisting of virtual data and real data.
+
+```javascript
+import { onSilentSubmitSuccess, stringifyVData } from '@alova/scene-*';
+
+//...
+// id is virtual data during initialization
+let id = /* todo virtual id */;
+
+// highlight-start
+// Binding listener silently submits the successful event to update the id, and returns the unbind function, don't forget to call the unbind function when the component is destroyed
+const unbindEvent = onSilentSubmitSuccess(event => {
+ const vDataId = stringifyVData(id);
+ if (event.vDataResponse[vDataId]) {
+ id = event.vDataResponse[vDataId];
+
+ // The following is to change the virtual id in the url to the actual id
+ history.replaceState(null, '', '?id=' + currentId);
+ }
+});
+// highlight-end
+```
+
+Here, the `event.vDataResponse` value is a collection of virtual data id and actual data, and its format is as follows:
+
+```javascript
+{
+ '[vd:aaaaaa]': { id: 1 },
+ '[vd:bbbbbb]': 1
+}
+```
+
+So far, we have completed all the content of a simple list of non-inductive interaction, but in other application scenarios such as editing applications, complex list management, etc., we may encounter more different needs. What else does alova have at this time? What are the features we can use? Please read the next chapter!
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md
similarity index 96%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md
index 7f531391b..07ee94e6e 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/09-what-more.md
@@ -1,342 +1,343 @@
----
-title: What more?
-sidebar_position: 90
----
-
-## Description of the role of virtual data
-
-In the previous chapters we used virtual data as the id placeholder, but its function is more than that, it can occupy any response data, for example, in a complex list, when creating a data item, the server needs to calculate additional Data, at this time, these additional data can also be occupied by virtual data, but this requires that the additional data needs to be returned together when the data item is created. See the following example:
-
-```javascript
-const { onSuccess, send } = useSQRequest(createOrEditData, {
- behavior: 'silent',
- immediate: false,
-
- // Construct the same data structure as the response data
- silentDefaultResponse: () => {
- return {
- id: '--',
- extra1: '',
- extra2: ''
- };
- }
-});
-onSuccess(event => {
- event.data.id; // virtual data
- event.data.extra1; //virtual data
- event.data.extra2; //virtual data
-});
-```
-
-## New events in useSQRequest
-
-In order to better monitor the behavior of requests in the queue, `useSQRequset` also provides the following 3 additional event monitoring functions, you can obtain the binding functions in the following ways.
-
-```javascript
-const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */);
-```
-
-###onBeforePushQueue
-
-silentMethod is an event before entering the request queue. It is valid when the behavior mode is `queue` or `silent`. You can return `false` in this event callback to prevent the current silentMethod from entering the queue. For example, you may want to replace the current silentMethod with another one. It can be done like this:
-
-```javascript
-//...
-onBeforePushQueue(event => {
- // Replace the old silentMethod with the specified id each time, reducing the number of requests
- const prevSumbmitMethod = getSilentMethod('temp' + id);
- if (event.silentMethod && prevSumbmitMethod) {
- prevSumbmitMethod.replace(event.silentMethod);
- return false;
- }
-});
-```
-
-###onPushedQueue
-
-silentMethod The event after entering the queue. It is valid when the behavior mode is `queue` or `silent`. If the queue is blocked in the **onBeforePushQueue** event, this function will not trigger.
-
-### onFallback
-
-Similar to the traditional optimistic ui solution, we also provide a request rollback event, which will be triggered when the request reaches the maximum number of retries or the retry judgment fails. You can use it to handle some rollback operations.
-
-:::warning Warning
-
-When the fallback event is bound, even if the behavior mode is `silent`, the request will no longer be persisted, and it will be lost after refreshing the page. This is because the persistent silentMethod usually needs to ensure completion, not Rollbacks allow the user to re-process, in which case the rollback function should not be used.
-
-:::
-
-## Save additional operation data
-
-When creating or editing a data item, the previous chapters only saved the echo data to `silentMethod.reviewData`, if there are some additional data that need to be recorded, such as the menu options of the edit page, etc., we also need to record them to ensure They can also be selected when the network is disconnected. At this time, these data are mounted on the silentMethod instance and persisted together.
-
-Generally speaking, you can save persistent data with any property name, but an error will be reported in typescript, so the `silentMethod.extraData` attribute is specified for you as the storage field for extra data, remember to use `silentMethod.save()` for persistence data.
-
-## Custom serializer
-
-By default, alova uses localStorage for silentMethod data persistence, so it will call `JSON.stringify` to convert to a string when persisting, but json data only supports basic data types, pure objects and arrays, if you want Serialize special data structures such as Date instances, RegExp instances, functions, and custom class instances. Alova supports custom serializers to handle them. How to convert it to a data structure supported by json when storing it? How to convert to the original object structure.
-
-```javascript
-const regExpSerializer = {
- // forward is called when serializing
- // Need to judge whether the data is the target value, if not, return undefined, indicating that it will not be processed
- forward: data => data instanceof RegExp ? data.source : undefined,
-
- // backward is called during deserialization, data is the value returned in forward
- backward: source => new RegExp(source);
-};
-
-bootSilentFactory({
- //...
- // use this serializer via serializers
- serializers: {
- customRegExp: regExpSerializer
- }
-})
-```
-
-SilentFactory provides Date and RegExp serializers by default, and you can also use the same key to override the default serializers
-
-```javascript
-const defaultSerializers = {
- Date: dateSerializer,
- RegExp: regExpSerializer
-};
-```
-
-[Read Date serializer source code](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts)
-
-## Manipulate the silent queue
-
-Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains relevant configuration for silent submission. Any number of silent queues can be generated, and it supports searching, modifying, and deleting silentMethod instances in the queue.
-
-### Using multiple silent queues
-
-Requests whose behavior mode is set to `queue` and `silent` will enter the silent queue. By default, the silentMethod instance will be assigned to the **default** queue. When it needs to be assigned to other queues, it can be assigned in _useSQRequest_ Specify the `queue` parameter.
-
-```javascript
-useSQRequest(createOrEditTodo, {
- //...
- // Specify the silentMethod instance to enter the queue named customQueue
- queue: 'customQueue',
- behavior: 'silent'
-});
-```
-
-You can also specify `queue` as a function to return the name of the queue. This function will be called every time a request is made, and the function parameters come from the send function.
-
-```javascript
-const { send } = useSQRequest(createOrEditTodo, {
- //...
- // Determine whether to enter the customQueue queue according to useCustomQueue
- queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'),
- behavior: 'silent',
- immediate: false
-});
-const handleClick = () => {
- send(true);
-};
-```
-
-### Find silentMethod
-
-In the previous [data compensation](/tutorial/strategy/sensorless-data-interaction/data-compensation), we used [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) to find the silentMethod of the specified queue instance, it will return all matching silentMethod instances, here are two more ways to find the queue:
-
-#### Find a silentMethod instance
-
-Use `getSilentMethod` to query the first matching silentMethod instance, the usage is the same as [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods).
-
-```typescript
-function filterSilentMethods(
- methodNameMatcher?: string | number | RegExp,
- queueName?: string,
- filterActive?: boolean
-): SilentMethod | undefined;
-```
-
-#### Custom Lookup
-
-Customize the lookup through the exported `silentQueueMap` queue collection, the data structure of `silentQueueMap` is:
-
-```javascript
-const silentQueueMap = {
- default: [silentMethod1, silentMethod2 /* ... */],
- queueName1: [silentMethod3, silentMethod4 /* ... */],
- queueName2: [silentMethod5, silentMethod6 /* ... */]
- //...
-};
-```
-
-### Change the silentMethod in the queue
-
-After finding the silentMethod instance you want, you can manipulate these waiting silentMethod instances.
-
-#### replace silentMethod
-
-Call `silentMethod.replace` to replace a silentMethod with another silentMethod in the queue.
-
-```javascript
-oldSilentMethod.replace(newSilentMethod);
-```
-
-#### remove silentMethod
-
-Call `silentMethod.remove` to remove the current silentMethod from the queue.
-
-```javascript
-oldSilentMethod.remove();
-```
-
-#### Use silentQueueMap to change silentMethod
-
-You can also access `silentQueueMap` to custom change any data of any queue.
-
-```javascript
-import { silentQueueMap } from '@alova/scene-*';
-
-// Modify all silentMethods in the default queue
-silentQueueMap.default.forEach(silentMethodItem => {
- //...
-});
-```
-
-## Queue request delay
-
-Some applications need to submit data frequently, such as editor-type applications, which are saved in real time during the editing process without aborting the user's use. When using silent submission in this type of application, more request information will be generated, not only It will fill up the front-end cache and make the server receive too many requests. At this time, we may no longer need to synchronize all save operations, but send an operation within a period of time. There will be the following two solutions:
-
-1. Throttle the editing operation, and only initiate one submission within n seconds. This solution may lose the operation records during the delay period, resulting in only the status of the last submission being obtained when refreshing;
-2. Delay the save request in the queue, and only keep the latest request information during the delay time, so that you can reduce the request while retaining the latest editing status;
-
-By default, the silentMethod in the queue will send the request immediately after the last response. We can set the delay sending time of the silentMethod through `requestWait` at startup, and keep the latest silentMethod by manipulating the queue during this time.
-
-### Set request delay
-
-You can set the delay time for the specified queue, or you can set multiple queues at once.
-
-```javascript
-bootSilentFactory({
- alova: alovaInst,
- // highlight-start
- requestWait: [
- // Each silentMethod in the customQueue queue will delay 5000ms to initiate a request
- { queue: 'customQueue', wait: 5000 },
-
- //Use a regular expression to uniformly set a 3000ms delay request time for the queue whose name is prefixed with delay
- { queue: /^delay/, wait: 3000 }
- ]
- // highlight-end
-});
-```
-
-When `requestWait` is directly set to a number, it is valid for the default queue by default.
-
-```javascript
-bootSilentFactory({
- alova: alovaInst,
- // highlight-start
- // Each silentMethod in the default queue will delay 5000ms to initiate a request
- requestWait: 5000
- // highlight-end
-});
-```
-
-### Dynamically set the request delay
-
-Many times we only want a specific silentMethod in the queue to set a delay request. At this time, a function can be used to dynamically set the request delay. Each silentMethod of the specified queue will call this function before initiating a request to determine the delay time.
-
-```javascript
-bootSilentFactory({
- alova: alovaInst,
- // highlight-start
- requestWait: [
- {
- queue: /^delay,
- // Only delay 5000ms for post requests with url /edit
- wait: (silentMethod, queueName) => {
- const { type, url, data } = silentMethod.entity;
- if (type === 'POST' && url === '/edit') {
- return 5000;
- }
- }
- },
- ]
- // highlight-end
-});
-```
-
-Similarly, when `requestWait` is set directly to a function, it defaults to the default queue.
-
-```javascript
-bootSilentFactory({
- alova: alovaInst,
- // highlight-start
- requestWait: (silentMethod, queueName) => {
- const { type, url, data } = silentMethod.entity;
- if (type === 'POST' && url === '/edit') {
- return 5000;
- }
- }
- // highlight-end
-});
-```
-
-## Dynamically set the method name
-
-For easy search, if you need to dynamically set the name of the method in silentMethod, you can call setName.
-
-```javascript
-// Reset the name of silentMethod on successful request
-onSuccess(({ data, silentMethod }) => {
- silentMethod.entity.setName('name' + data.id);
-});
-```
-
-## Global silent submit event
-
-In the previous chapter, we touched `onSilentSubmitSuccess`, we provided a total of 5 global events.
-
-### onSilentSubmitBoot
-
-Silent factory start event, triggered after the silent factory is started.
-
-```typescript
-function onSilentSubmitBoot(handler: () => void): OffEventCallback;
-```
-
-###onBeforeSilentSubmit
-
-Fired before a silentMethod request with `behavior=silent`.
-
-```typescript
-function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback;
-```
-
-###onSilentSubmitSuccess
-
-Fired when a silentMethod request with `behavior=silent` succeeds.
-
-```typescript
-function onSilentSubmitSuccess(handler: (event: GlobalSQSuccessEvent) => void): OffEventCallback;
-```
-
-### onSilentSubmitError
-
-Fired when the silentMethod request with `behavior=silent` fails, but the maximum number of retries has not been reached.
-
-```typescript
-function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback;
-```
-
-### onSilentSubmitFail
-
-When the silentMethod of `behavior=silent` encounters a request failure, the following 3 situations will trigger this event:
-
-1. Triggered when the request reaches the maximum number of retries;
-2. When the number of retries is not set, the first request failure will trigger;
-3. Triggered when the request has not reached the maximum number of retries, but the retry is judged to be no more retries;
-
-```typescript
-function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback;
-```
-
-The above event binding functions will return the unbinding function, and you can unbind the event before the component is unmounted.
+---
+title: What more?
+---
+
+## Description of the role of virtual data
+
+In the previous chapters we used virtual data as the id placeholder, but its function is more than that, it can occupy any response data, for example, in a complex list, when creating a data item, the server needs to calculate additional Data, at this time, these additional data can also be occupied by virtual data, but this requires that the additional data needs to be returned together when the data item is created. See the following example:
+
+```javascript
+const { onSuccess, send } = useSQRequest(createOrEditData, {
+ behavior: 'silent',
+ immediate: false,
+
+ // Construct the same data structure as the response data
+ silentDefaultResponse: () => {
+ return {
+ id: '--',
+ extra1: '',
+ extra2: ''
+ };
+ }
+});
+onSuccess(event => {
+ event.data.id; // virtual data
+ event.data.extra1; //virtual data
+ event.data.extra2; //virtual data
+});
+```
+
+## New events in useSQRequest
+
+In order to better monitor the behavior of requests in the queue, `useSQRequset` also provides the following 3 additional event monitoring functions, you can obtain the binding functions in the following ways.
+
+```javascript
+const { onBeforePushQueue, onPushedQueue, onFallback } = useSQRequest(/* ... */);
+```
+
+###onBeforePushQueue
+
+silentMethod is an event before entering the request queue. It is valid when the behavior mode is `queue` or `silent`. You can return `false` in this event callback to prevent the current silentMethod from entering the queue. For example, you may want to replace the current silentMethod with another one. It can be done like this:
+
+```javascript
+//...
+onBeforePushQueue(event => {
+ // Replace the old silentMethod with the specified id each time, reducing the number of requests
+ const prevSumbmitMethod = getSilentMethod('temp' + id);
+ if (event.silentMethod && prevSumbmitMethod) {
+ prevSumbmitMethod.replace(event.silentMethod);
+ return false;
+ }
+});
+```
+
+###onPushedQueue
+
+silentMethod The event after entering the queue. It is valid when the behavior mode is `queue` or `silent`. If the queue is blocked in the **onBeforePushQueue** event, this function will not trigger.
+
+### onFallback
+
+Similar to the traditional optimistic ui solution, we also provide a request rollback event, which will be triggered when the request reaches the maximum number of retries or the retry judgment fails. You can use it to handle some rollback operations.
+
+:::warning Warning
+
+When the fallback event is bound, even if the behavior mode is `silent`, the request will no longer be persisted, and it will be lost after refreshing the page. This is because the persistent silentMethod usually needs to ensure completion, not Rollbacks allow the user to re-process, in which case the rollback function should not be used.
+
+:::
+
+## Save additional operation data
+
+When creating or editing a data item, the previous chapters only saved the echo data to `silentMethod.reviewData`, if there are some additional data that need to be recorded, such as the menu options of the edit page, etc., we also need to record them to ensure They can also be selected when the network is disconnected. At this time, these data are mounted on the silentMethod instance and persisted together.
+
+Generally speaking, you can save persistent data with any property name, but an error will be reported in typescript, so the `silentMethod.extraData` attribute is specified for you as the storage field for extra data, remember to use `silentMethod.save()` for persistence data.
+
+## Custom serializer
+
+By default, alova uses localStorage for silentMethod data persistence, so it will call `JSON.stringify` to convert to a string when persisting, but json data only supports basic data types, pure objects and arrays, if you want Serialize special data structures such as Date instances, RegExp instances, functions, and custom class instances. Alova supports custom serializers to handle them. How to convert it to a data structure supported by json when storing it? How to convert to the original object structure.
+
+```javascript
+const regExpSerializer = {
+ // forward is called when serializing
+ // Need to judge whether the data is the target value, if not, return undefined, indicating that it will not be processed
+ forward: data => data instanceof RegExp ? data.source : undefined,
+
+ // backward is called during deserialization, data is the value returned in forward
+ backward: source => new RegExp(source);
+};
+
+bootSilentFactory({
+ //...
+ // use this serializer via serializers
+ serializers: {
+ customRegExp: regExpSerializer
+ }
+})
+```
+
+SilentFactory provides Date and RegExp serializers by default, and you can also use the same key to override the default serializers
+
+```javascript
+const defaultSerializers = {
+ Date: dateSerializer,
+ RegExp: regExpSerializer
+};
+```
+
+[Read Date serializer source code](https://github.com/alovajs/scene/blob/main/src/hooks/silent/serializer/date.ts)
+
+## Manipulate the silent queue
+
+Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains relevant configuration for silent submission. Any number of silent queues can be generated, and it supports searching, modifying, and deleting silentMethod instances in the queue.
+
+### Using multiple silent queues
+
+Requests whose behavior mode is set to `queue` and `silent` will enter the silent queue. By default, the silentMethod instance will be assigned to the **default** queue. When it needs to be assigned to other queues, it can be assigned in _useSQRequest_ Specify the `queue` parameter.
+
+```javascript
+useSQRequest(createOrEditTodo, {
+ //...
+ // Specify the silentMethod instance to enter the queue named customQueue
+ queue: 'customQueue',
+ behavior: 'silent'
+});
+```
+
+You can also specify `queue` as a function to return the name of the queue. This function will be called every time a request is made, and the function parameters come from the send function.
+
+```javascript
+const { send } = useSQRequest(createOrEditTodo, {
+ //...
+ // Determine whether to enter the customQueue queue according to useCustomQueue
+ queue: useCustomQueue => (useCustomQueue ? 'customQueue' : 'default'),
+ behavior: 'silent',
+ immediate: false
+});
+const handleClick = () => {
+ send(true);
+};
+```
+
+### Find silentMethod
+
+In the previous [data compensation](/tutorial/strategy/sensorless-data-interaction/data-compensation), we used [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods) to find the silentMethod of the specified queue instance, it will return all matching silentMethod instances, here are two more ways to find the queue:
+
+#### Find a silentMethod instance
+
+Use `getSilentMethod` to query the first matching silentMethod instance, the usage is the same as [filterSilentMethods](/tutorial/strategy/sensorless-data-interaction/data-compensation#filtersilentmethods).
+
+```typescript
+function filterSilentMethods(
+ methodNameMatcher?: string | number | RegExp,
+ queueName?: string,
+ filterActive?: boolean
+): SilentMethod | undefined;
+```
+
+#### Custom Lookup
+
+Customize the lookup through the exported `silentQueueMap` queue collection, the data structure of `silentQueueMap` is:
+
+```javascript
+const silentQueueMap = {
+ default: [silentMethod1, silentMethod2 /* ... */],
+ queueName1: [silentMethod3, silentMethod4 /* ... */],
+ queueName2: [silentMethod5, silentMethod6 /* ... */]
+ //...
+};
+```
+
+### Change the silentMethod in the queue
+
+After finding the silentMethod instance you want, you can manipulate these waiting silentMethod instances.
+
+#### replace silentMethod
+
+Call `silentMethod.replace` to replace a silentMethod with another silentMethod in the queue.
+
+```javascript
+oldSilentMethod.replace(newSilentMethod);
+```
+
+#### remove silentMethod
+
+Call `silentMethod.remove` to remove the current silentMethod from the queue.
+
+```javascript
+oldSilentMethod.remove();
+```
+
+#### Use silentQueueMap to change silentMethod
+
+You can also access `silentQueueMap` to custom change any data of any queue.
+
+```javascript
+import { silentQueueMap } from '@alova/scene-*';
+
+// Modify all silentMethods in the default queue
+silentQueueMap.default.forEach(silentMethodItem => {
+ //...
+});
+```
+
+## Queue request delay
+
+Some applications need to submit data frequently, such as editor-type applications, which are saved in real time during the editing process without aborting the user's use. When using silent submission in this type of application, more request information will be generated, not only It will fill up the front-end cache and make the server receive too many requests. At this time, we may no longer need to synchronize all save operations, but send an operation within a period of time. There will be the following two solutions:
+
+1. Throttle the editing operation, and only initiate one submission within n seconds. This solution may lose the operation records during the delay period, resulting in only the status of the last submission being obtained when refreshing;
+2. Delay the save request in the queue, and only keep the latest request information during the delay time, so that you can reduce the request while retaining the latest editing status;
+
+By default, the silentMethod in the queue will send the request immediately after the last response. We can set the delay sending time of the silentMethod through `requestWait` at startup, and keep the latest silentMethod by manipulating the queue during this time.
+
+### Set request delay
+
+You can set the delay time for the specified queue, or you can set multiple queues at once.
+
+```javascript
+bootSilentFactory({
+ alova: alovaInst,
+ // highlight-start
+ requestWait: [
+ // Each silentMethod in the customQueue queue will delay 5000ms to initiate a request
+ { queue: 'customQueue', wait: 5000 },
+
+ //Use a regular expression to uniformly set a 3000ms delay request time for the queue whose name is prefixed with delay
+ { queue: /^delay/, wait: 3000 }
+ ]
+ // highlight-end
+});
+```
+
+When `requestWait` is directly set to a number, it is valid for the default queue by default.
+
+```javascript
+bootSilentFactory({
+ alova: alovaInst,
+ // highlight-start
+ // Each silentMethod in the default queue will delay 5000ms to initiate a request
+ requestWait: 5000
+ // highlight-end
+});
+```
+
+### Dynamically set the request delay
+
+Many times we only want a specific silentMethod in the queue to set a delay request. At this time, a function can be used to dynamically set the request delay. Each silentMethod of the specified queue will call this function before initiating a request to determine the delay time.
+
+```javascript
+bootSilentFactory({
+ alova: alovaInst,
+ // highlight-start
+ requestWait: [
+ {
+ queue: /^delay,
+ // Only delay 5000ms for post requests with url /edit
+ wait: (silentMethod, queueName) => {
+ const { type, url, data } = silentMethod.entity;
+ if (type === 'POST' && url === '/edit') {
+ return 5000;
+ }
+ }
+ },
+ ]
+ // highlight-end
+});
+```
+
+Similarly, when `requestWait` is set directly to a function, it defaults to the default queue.
+
+```javascript
+bootSilentFactory({
+ alova: alovaInst,
+ // highlight-start
+ requestWait: (silentMethod, queueName) => {
+ const { type, url, data } = silentMethod.entity;
+ if (type === 'POST' && url === '/edit') {
+ return 5000;
+ }
+ }
+ // highlight-end
+});
+```
+
+## Dynamically set the method name
+
+For easy search, if you need to dynamically set the name of the method in silentMethod, you can call setName.
+
+```javascript
+// Reset the name of silentMethod on successful request
+onSuccess(({ data, silentMethod }) => {
+ silentMethod.entity.setName('name' + data.id);
+});
+```
+
+## Global silent submit event
+
+In the previous chapter, we touched `onSilentSubmitSuccess`, we provided a total of 5 global events.
+
+### onSilentSubmitBoot
+
+Silent factory start event, triggered after the silent factory is started.
+
+```typescript
+function onSilentSubmitBoot(handler: () => void): OffEventCallback;
+```
+
+###onBeforeSilentSubmit
+
+Fired before a silentMethod request with `behavior=silent`.
+
+```typescript
+function onBeforeSilentSubmit(handler: (event: GlobalSQEvent)): OffEventCallback;
+```
+
+###onSilentSubmitSuccess
+
+Fired when a silentMethod request with `behavior=silent` succeeds.
+
+```typescript
+function onSilentSubmitSuccess(
+ handler: (event: GlobalSQSuccessEvent) => void
+): OffEventCallback;
+```
+
+### onSilentSubmitError
+
+Fired when the silentMethod request with `behavior=silent` fails, but the maximum number of retries has not been reached.
+
+```typescript
+function onSilentSubmitError(handler: (event: GlobalSQErrorEvent) => void): OffEventCallback;
+```
+
+### onSilentSubmitFail
+
+When the silentMethod of `behavior=silent` encounters a request failure, the following 3 situations will trigger this event:
+
+1. Triggered when the request reaches the maximum number of retries;
+2. When the number of retries is not set, the first request failure will trigger;
+3. Triggered when the request has not reached the maximum number of retries, but the retry is judged to be no more retries;
+
+```typescript
+function onSilentSubmitFail(handler: (event: GlobalSQFailEvent) => void): OffEventCallback;
+```
+
+The above event binding functions will return the unbinding function, and you can unbind the event before the component is unmounted.
diff --git a/docs/tutorial/05-strategy/01-sensorless-data-interaction/README.md b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md
similarity index 98%
rename from docs/tutorial/05-strategy/01-sensorless-data-interaction/README.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md
index 5b703c10d..d7b8f1bb7 100644
--- a/docs/tutorial/05-strategy/01-sensorless-data-interaction/README.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/README.md
@@ -1,146 +1,146 @@
----
-title: Sensorless data interaction - Overview
----
-
-Non-inductive data interaction means that when users interact with the application, relevant content can be displayed immediately without waiting, or the operation result can be displayed without waiting when submitting information, just like interacting with local data, thereby greatly improving the smoothness of the application It allows users to not feel the lag caused by data transmission.
-
-This is not new. The concept of optimistic update existed before 2015. It refers to displaying the submission results to the interface before the server responds. It is based on the assumption that most submissions are successful. The opposite is a conservative update, that is, the server will display a wait state before responding until the request is completed. In terms of handling failures, the current optimistic update solution is usually handled through fallback, such as the following sample code:
-
-```javascript
-const list = [];
-const data = {};
-addTodo(data).catch(() => {
- list = list.filter(item => item !== data);
-});
-list.push(data);
-```
-
-This can cause the following problems:
-
-1. Rollback will increase the user's understanding and operation costs;
-2. Request timing issues;
-3. If subsequent requests depend on this submission, this failure will make subsequent requests meaningless;
-4. Possible lost requests;
-
-After several months of program design and continuous iteration, alova has taken a big step in this area. In our program, the above problems have been solved, which can ensure the success of the request more stably. Although there are still technical limitations, But it has been applied in many scenarios. In our technical solution, the problems caused by network fluctuations can be reduced to a higher extent. Your application is still available in high-latency networks or even disconnected, and the latest data can still be maintained after refreshing the page.
-
-## Application scenarios
-
-Although non-inductive data interaction cannot be used on a large scale, it is very suitable in certain scenarios. During the exploration, we found at least including but not limited to the following scenarios for your reference.
-
-### Editor application
-
-Note-taking applications such as Evernote and canvas editing applications such as MasterGO need to meet the following requirements:
-
-1. When entering the note or drawing list, the list data will be pulled in full, and the local cache data will be used next time;
-2. Real-time synchronization to the server during the editing process, and the synchronization process occurs in the background, which will not affect the normal use of users;
-3. You can continue to use it even when the network is poor or disconnected;
-
-:::info example
-
-We provide a [note application example](/tutorial/example/silent-submit-notes), you can enter the experience.
-
-:::
-
-### Setup module
-
-The setting module composed of commonly used switches and selectors needs to realize the requirement that the user operation is synchronized to the server in real time, and the submission status is no longer displayed, but the latest status after the operation is directly displayed.
-
-:::info example
-
-We provide a [setting page example](/tutorial/example/silent-submit-setting), you can enter the experience.
-
-:::
-
-### Simple list management
-
-The data we fill in when creating a list item is enough for the display of the list page, which is called a simple list. For example, a student list page displays the three data of the student's name, gender, and grade. These three data are required when creating a student fill in. In a simple list the following requirements will be fulfilled:
-
-1. Immediately display the latest status on the list page when adding, editing and deleting list items, no need to display it after the submission is completed, and it is not limited by network fluctuations;
-2. When the page is refreshed, the list page is always kept up to date;
-
-:::info example
-
-We provide a [simple list page example](/tutorial/example/silent-submit-simple-list), you can enter the experience.
-
-:::
-
-### Complex list management
-
-A complex list means that the data filled in when creating a list item is not enough for display on the list page, and additional data needs to be generated according to the calculation of the server. For example, a Todo list page needs to list specific executions in addition to displaying basic information. date, but only the execution date range and related rules are specified on the creation page, so the execution date is calculated and generated by the server based on the date range and rules.
-
-The following requirements will be fulfilled in a complex list:
-
-1. Immediately display the latest status on the list page when adding, editing and deleting list items, and update the data calculated by the server to this list item after the server responds;
-2. When the page is refreshed, the list page is always kept up to date;
-
-:::info example
-
-Stay tuned for complex list examples...
-
-:::
-
-### Free Mode
-
-In the above scenarios, you may want to judge whether to use the non-inductive interaction strategy or the most common conservative request strategy based on a condition. The requirements are as follows:
-
-1. When the network status is good, or paying users will use the non-sensing interaction strategy, but when the network fluctuates greatly, or free users cannot enjoy the non-sensing interaction strategy;
-2. Strategies can be switched freely;
-
-:::info example
-
-In the above examples, you can experience the free switching strategy
-
-:::
-
-## Not recommended application scenarios
-
-### Information sharing class
-
-The submitted information needs to be synchronized to others, such as order information. This type of information has high real-time requirements, and we should ensure that the submission is successful.
-
-### Complex data interaction class
-
-Complex data interaction refers to the mixed editing and filtering of data, such as adding, editing, deleting and filtering a certain list. In this case, Alova cannot currently support it well. In subsequent versions Will try to solve this puzzle too.
-
-## Technical solutions
-
-In the technical solution of non-inductive data interaction, alova has implemented data pre-fetching and silent submission respectively. Next, let's understand these two technical solutions.
-
-:::info
-
-Please make sure you have mastered the following chapters before reading
-
-- [Basic Learning](/tutorial/getting-started)
-
-:::
-
-### Data pre-fetching
-
-In html, you may have seen such a tag ``, which tells the browser to preload the style file when it is idle, and put it in the cache In , when you really need to use it, you can take it out of the cache. Alova also uses a similar scheme to pre-fetch the required data through [useFetcher](/tutorial/advanced/use-fetcher), and it will be stored locally. in cache. You can predict the content that the user needs to read under any circumstances, and then pre-fetch the corresponding content. For example, the content of the next page can be pre-loaded in the process page, or the user stays on a button 200ms, we can pre-fetch the data needed for the next interface, which is similar to **Next.js** page preloading.
-
-We provide a [preloaded example](/tutorial/example/prefetch), you can enter the experience.
-
-### Silent submit
-
-Silent submission is a mechanism of submitting and responding. In the scheme, the completion of submission will be guaranteed, so it can be regarded as a safer optimistic update scheme. Silent submission mainly uses **Silent Queue** to persist request information and ensure request timing issues. **Virtual data** is used as a placeholder for server response data, which is replaced with actual response data when the request is completed. , through these two technologies, localized data creation is realized, and operations such as editing and deleting of newly created data are realized, even if the created data has not yet been submitted successfully on the server side. In order to keep development costs to a minimum, this is done automatically in alova.
-
-### Quiet Queue
-
-Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains related configurations for silent submission, such as _unique id_, _error retry parameters_, etc. The requests in the queue will only initiate the next request after the previous response, thus ensuring the timing of the requests in the queue. You can put dependent requests in the same queue, which also ensures data consistency.
-
-![Silent queue](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png)
-
-In the scheme, three behavior modes of `queue`, `silent`, and `static` are provided respectively, which are used to distinguish what kind of behavior a request needs to perform.
-
-- queue: The request will enter the silent queue, but it will not be persisted. It will wait for the previous request to complete before sending the request. The response callback will be triggered after the response. It is generally used for data acquisition that depends on the previous request;
-- silent: The request will enter the silent queue and be persisted, and then trigger the response callback immediately. In this behavior mode, onSuccess will receive virtual data, and onError will never be triggered. Use this pattern;
-- static: the request will not enter the silent queue, nor will it be persisted, it will issue the request immediately, and this mode can be used when silent submission is disabled;
-
-### virtual data
-
-In the submit-to-response mechanism, virtual data plays an important role. It means that before the server actually responds, it is used as a placeholder for the response data, and through the tracing mechanism, even if the virtual data is distributed in various locations of the application, Can be automatically replaced with the actual response data after the response. At the same time, it also plays an important role in the silent queue. It can identify the dependencies of requests in the queue, and replace the dependent data with actual data after the dependencies respond. For example, when creating a piece of data, it will return the id of this data. When the service When the terminal has not responded, the user performs a delete operation, and the id needs to be used as the delete identifier. At this time, the delete request will depend on the creation request. Before creating a request response, the virtual data will be used as an id placeholder as a parameter for deletion, and the virtual data id will be replaced after creating a request response, so that the deletion request can be completed.
-
-![virtual data](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png)
-
-Next, we will learn more about the characteristics of virtual data.
+---
+title: Sensorless data interaction - Overview
+---
+
+Non-inductive data interaction means that when users interact with the application, relevant content can be displayed immediately without waiting, or the operation result can be displayed without waiting when submitting information, just like interacting with local data, thereby greatly improving the smoothness of the application It allows users to not feel the lag caused by data transmission.
+
+This is not new. The concept of optimistic update existed before 2015. It refers to displaying the submission results to the interface before the server responds. It is based on the assumption that most submissions are successful. The opposite is a conservative update, that is, the server will display a wait state before responding until the request is completed. In terms of handling failures, the current optimistic update solution is usually handled through fallback, such as the following sample code:
+
+```javascript
+const list = [];
+const data = {};
+addTodo(data).catch(() => {
+ list = list.filter(item => item !== data);
+});
+list.push(data);
+```
+
+This can cause the following problems:
+
+1. Rollback will increase the user's understanding and operation costs;
+2. Request timing issues;
+3. If subsequent requests depend on this submission, this failure will make subsequent requests meaningless;
+4. Possible lost requests;
+
+After several months of program design and continuous iteration, alova has taken a big step in this area. In our program, the above problems have been solved, which can ensure the success of the request more stably. Although there are still technical limitations, But it has been applied in many scenarios. In our technical solution, the problems caused by network fluctuations can be reduced to a higher extent. Your application is still available in high-latency networks or even disconnected, and the latest data can still be maintained after refreshing the page.
+
+## Application scenarios
+
+Although non-inductive data interaction cannot be used on a large scale, it is very suitable in certain scenarios. During the exploration, we found at least including but not limited to the following scenarios for your reference.
+
+### Editor application
+
+Note-taking applications such as Evernote and canvas editing applications such as MasterGO need to meet the following requirements:
+
+1. When entering the note or drawing list, the list data will be pulled in full, and the local cache data will be used next time;
+2. Real-time synchronization to the server during the editing process, and the synchronization process occurs in the background, which will not affect the normal use of users;
+3. You can continue to use it even when the network is poor or disconnected;
+
+:::info example
+
+We provide a [note application example](/tutorial/example/silent-submit-notes), you can enter the experience.
+
+:::
+
+### Setup module
+
+The setting module composed of commonly used switches and selectors needs to realize the requirement that the user operation is synchronized to the server in real time, and the submission status is no longer displayed, but the latest status after the operation is directly displayed.
+
+:::info example
+
+We provide a [setting page example](/tutorial/example/silent-submit-setting), you can enter the experience.
+
+:::
+
+### Simple list management
+
+The data we fill in when creating a list item is enough for the display of the list page, which is called a simple list. For example, a student list page displays the three data of the student's name, gender, and grade. These three data are required when creating a student fill in. In a simple list the following requirements will be fulfilled:
+
+1. Immediately display the latest status on the list page when adding, editing and deleting list items, no need to display it after the submission is completed, and it is not limited by network fluctuations;
+2. When the page is refreshed, the list page is always kept up to date;
+
+:::info example
+
+We provide a [simple list page example](/tutorial/example/silent-submit-simple-list), you can enter the experience.
+
+:::
+
+### Complex list management
+
+A complex list means that the data filled in when creating a list item is not enough for display on the list page, and additional data needs to be generated according to the calculation of the server. For example, a Todo list page needs to list specific executions in addition to displaying basic information. date, but only the execution date range and related rules are specified on the creation page, so the execution date is calculated and generated by the server based on the date range and rules.
+
+The following requirements will be fulfilled in a complex list:
+
+1. Immediately display the latest status on the list page when adding, editing and deleting list items, and update the data calculated by the server to this list item after the server responds;
+2. When the page is refreshed, the list page is always kept up to date;
+
+:::info example
+
+Stay tuned for complex list examples...
+
+:::
+
+### Free Mode
+
+In the above scenarios, you may want to judge whether to use the non-inductive interaction strategy or the most common conservative request strategy based on a condition. The requirements are as follows:
+
+1. When the network status is good, or paying users will use the non-sensing interaction strategy, but when the network fluctuates greatly, or free users cannot enjoy the non-sensing interaction strategy;
+2. Strategies can be switched freely;
+
+:::info example
+
+In the above examples, you can experience the free switching strategy
+
+:::
+
+## Not recommended application scenarios
+
+### Information sharing class
+
+The submitted information needs to be synchronized to others, such as order information. This type of information has high real-time requirements, and we should ensure that the submission is successful.
+
+### Complex data interaction class
+
+Complex data interaction refers to the mixed editing and filtering of data, such as adding, editing, deleting and filtering a certain list. In this case, Alova cannot currently support it well. In subsequent versions Will try to solve this puzzle too.
+
+## Technical solutions
+
+In the technical solution of non-inductive data interaction, alova has implemented data pre-fetching and silent submission respectively. Next, let's understand these two technical solutions.
+
+:::info
+
+Please make sure you have mastered the following chapters before reading
+
+- [Basic Learning](/tutorial/getting-started)
+
+:::
+
+### Data pre-fetching
+
+In html, you may have seen such a tag ``, which tells the browser to preload the style file when it is idle, and put it in the cache In , when you really need to use it, you can take it out of the cache. Alova also uses a similar scheme to pre-fetch the required data through [useFetcher](/tutorial/advanced/use-fetcher), and it will be stored locally. in cache. You can predict the content that the user needs to read under any circumstances, and then pre-fetch the corresponding content. For example, the content of the next page can be pre-loaded in the process page, or the user stays on a button 200ms, we can pre-fetch the data needed for the next interface, which is similar to **Next.js** page preloading.
+
+We provide a [preloaded example](/tutorial/example/prefetch), you can enter the experience.
+
+### Silent submit
+
+Silent submission is a mechanism of submitting and responding. In the scheme, the completion of submission will be guaranteed, so it can be regarded as a safer optimistic update scheme. Silent submission mainly uses **Silent Queue** to persist request information and ensure request timing issues. **Virtual data** is used as a placeholder for server response data, which is replaced with actual response data when the request is completed. , through these two technologies, localized data creation is realized, and operations such as editing and deleting of newly created data are realized, even if the created data has not yet been submitted successfully on the server side. In order to keep development costs to a minimum, this is done automatically in alova.
+
+### Quiet Queue
+
+Silent queues are used to ensure the timing of requests. We can create queues arbitrarily, and all requests entering the queue will be stored in the queue in the form of **SilentMethod** instances. Each **SilentMethod** not only contains request information, but also Contains related configurations for silent submission, such as _unique id_, _error retry parameters_, etc. The requests in the queue will only initiate the next request after the previous response, thus ensuring the timing of the requests in the queue. You can put dependent requests in the same queue, which also ensures data consistency.
+
+![Silent queue](https://user-images.githubusercontent.com/29848971/220057005-dd467392-4a43-45a7-90dc-999dd1d95531.png)
+
+In the scheme, three behavior modes of `queue`, `silent`, and `static` are provided respectively, which are used to distinguish what kind of behavior a request needs to perform.
+
+- queue: The request will enter the silent queue, but it will not be persisted. It will wait for the previous request to complete before sending the request. The response callback will be triggered after the response. It is generally used for data acquisition that depends on the previous request;
+- silent: The request will enter the silent queue and be persisted, and then trigger the response callback immediately. In this behavior mode, onSuccess will receive virtual data, and onError will never be triggered. Use this pattern;
+- static: the request will not enter the silent queue, nor will it be persisted, it will issue the request immediately, and this mode can be used when silent submission is disabled;
+
+### virtual data
+
+In the submit-to-response mechanism, virtual data plays an important role. It means that before the server actually responds, it is used as a placeholder for the response data, and through the tracing mechanism, even if the virtual data is distributed in various locations of the application, Can be automatically replaced with the actual response data after the response. At the same time, it also plays an important role in the silent queue. It can identify the dependencies of requests in the queue, and replace the dependent data with actual data after the dependencies respond. For example, when creating a piece of data, it will return the id of this data. When the service When the terminal has not responded, the user performs a delete operation, and the id needs to be used as the delete identifier. At this time, the delete request will depend on the creation request. Before creating a request response, the virtual data will be used as an id placeholder as a parameter for deletion, and the virtual data id will be replaced after creating a request response, so that the deletion request can be completed.
+
+![virtual data](https://user-images.githubusercontent.com/29848971/220005505-d30b7ae2-ddd0-4080-81a4-65c9be2cb0bd.png)
+
+Next, we will learn more about the characteristics of virtual data.
diff --git a/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json
new file mode 100644
index 000000000..42291c7e5
--- /dev/null
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/01-sensorless-data-interaction/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Sensorless data interaction"
+}
diff --git a/docs/tutorial/05-strategy/02-usePagination.md b/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md
similarity index 94%
rename from docs/tutorial/05-strategy/02-usePagination.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md
index 231485512..37ef09e95 100644
--- a/docs/tutorial/05-strategy/02-usePagination.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/02-usePagination.md
@@ -1,727 +1,732 @@
----
-title: Pagination request strategy
-sidebar_position: 20
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info stragety type
-
-use hook
-
-:::
-
-> Before using extension hooks, make sure you are familiar with basic usage of alova.
-
-A hook designed for paging scenarios, which can help you automatically manage paging data, preload data, reduce unnecessary data refresh, improve fluency by 300%, and reduce coding difficulty by 50%\*\*. You can use it in the two paging scenarios of pull-down loading and page number flipping. This hook provides a wealth of features to help your application create better performance and more convenient paging functions.
-
-## Example
-
-[page list](/tutorial/example/paginated-list)
-
-[Pull down to load more](/tutorial/example/load-more)
-
-## Features
-
-- ✨ Rich and comprehensive paging status;
-- ✨ Rich and comprehensive pagination events;
-- ✨ Change page, pageSize to automatically get specified paging data;
-- ✨Data caching, no need to repeatedly request list data of the same parameters;
-- ✨ Front and back pages are preloaded, no waiting for page turning;
-- ✨Search condition monitoring automatically reacquires pages;
-- ✨ Support adding, editing and deleting list data;
-- ✨ Support refreshing the data of the specified page without reset;
-- ✨ Request-level search anti-shake, no need to maintain by yourself;
-
-## Install
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-## Usage
-
-### Display list data
-
-
-
-
-```html
-
-
- {{ item.name }}
-
-
-
-
- There are {{ pageCount }} pages
- A total of {{ total }} data
-
-
-
-```
-
-
-
-
-```jsx
-import { queryStudents } from './api.js';
-import { usePagination } from '@alova/scene-react';
-
-const App = () => {
- const {
- // loading state
- loading,
-
- // list data
- data,
-
- // is it the last page
- // This parameter can be used to determine whether it needs to be loaded during pull-down loading
- isLastPage,
-
- // The current page number, changing this page number will automatically trigger the request
- page: [page, setPage],
-
- // Number of data items per page
- pageSize: [page, setPageSize],
-
- // number of paging pages
- pageCount,
-
- // total amount of data
- total
- } = usePagination(
- // Method instance acquisition function, it will receive page and pageSize, and return a Method instance
- (page, pageSize) => queryStudents(page, pageSize),
- {
- // Initial data before the request (data format returned by the interface)
- initialData: {
- total: 0,
- data: []
- },
- initialPage: 1, // initial page number, default is 1
- initialPageSize: 10 // The initial number of data items per page, the default is 10
- }
- );
-
- // Turn to the previous page, the request will be sent automatically after the page value changes
- const handlePrevPage = () => {
- setPage(value => value - 1);
- };
-
- // Turn to the next page, the request will be sent automatically after the page value changes
- const handleNextPage = () => {
- setPage(value => value + 1);
- };
-
- // Change the number of pages, the request will be sent automatically after the pageSize value is changed
- const handleSetPageSize = () => {
- setPageSize(20);
- };
-
- return (
-
- {data.map(item => (
-
- {item.name}
-
- ))}
-
-
-
- There are {pageCount} pages
- A total of {total} pieces of data
-
-{/each}
-
-
-
-There are {pageCount} pages
-A total of {total} pieces of data
-```
-
-
-
-
-### Specify pagination data
-
-The data structure returned by each paging data interface is different, so we need to tell `usePagination` the list data and the total number of items separately, so as to help us manage the paging data.
-
-Suppose the data format returned by your paging interface is as follows:
-
-```typescript
-interface PaginationData {
- totalNumber: number;
- list: any[];
-}
-```
-
-At this point, you need to return the list data and the total number of items in the form of a function.
-
-```javascript
-usePagination((page, pageSize) => queryStudents(page, pageSize), {
- //...
- // highlight-start
- total: response => response.totalNumber,
- data: response => response.list
- // highlight-end
-});
-```
-
-If you don't specify the total and data callback functions, they will get data in the following ways by default.
-
-```javascript
-const total = response => response.total;
-const data = response => response.data;
-```
-
-:::warning Caution
-
-The data callback function must return a list of data, indicating the data set used in paging, and total is mainly used to calculate the current page number. If no number is returned in the total callback function, it will pass whether the number of requested lists is less than the pageSize value To determine whether the current page is the last page, which is generally used for pull-down loading.
-
-:::
-
-### Enable append mode
-
-By default, the original list data will be replaced when the page is turned, and the append mode will append the data of the next page to the bottom of the current list when the page is turned. A common usage scenario is to pull down to load more.
-
-```javascript
-usePagination((page, pageSize) => queryStudents(page, pageSize), {
- //...
- // highlight-start
- append: true
- // highlight-end
-});
-```
-
-### Preload adjacent page data
-
-In order to provide a better experience for pagination, when the previous and next pages of the current page meet the conditions, it will be automatically preloaded, so that when the user turns the page, the data can be displayed directly without waiting. This is the default behavior. If you don't want to preload the data of adjacent pages, you can turn it off in the following way.
-
-```javascript
-usePagination((page, pageSize) => queryStudents(page, pageSize), {
- //...
- // highlight-start
- preloadPreviousPage: false, // turn off preloading previous page data
- preloadNextPage: false // turn off preloading next page data
- // highlight-end
-});
-```
-
-:::warning preloading trigger conditions
-
-When preloading switch is turned on, the next page will not be loaded blindly. The following two conditions need to be met:
-
-1. Preloading is based on cache of alova. The method instance used for pagination must enable chache. By default, get requests will have 5 minutes of memory cache. If it is a non-get request or the cache is turned off globally, you also need to set the cache in this Method. In the instance, set `localCache` separately to enable cache.
-2. Based on the `total` and `pageSize` parameters, it is determined that there is still data on the next page.
-
-:::
-
-In addition to `onSuccess, onError, onComplete` request events, when preloading is triggered, you can also know the preloading status through `fetching`, and you can also listen to preloading request events through `onFetchSuccess, onFetchError, onFetchComplete`.
-
-```javascript
-const {
- // preload state
- fetching,
-
- // preload success event binding function
- onFetchSuccess,
-
- // preload error event binding function
- onFetchError,
-
- // Preloading complete event binding function
- onFetchComplete
-} = usePagination((page, pageSize) => queryStudents(page, pageSize), {
- //...
-});
-```
-
-### Listening filter conditions
-
-In many cases, the list needs to be filtered by conditions. At this time, the re-request can be triggered through the status monitoring of `usePagination`, which is the same as `useWatcher` provided by alova.
-
-For example, filter by student name, student grade.
-
-
-
-
-```html
-
-
-
-
-
-
-
-
-
-```
-
-
-
-
-```jsx
-import { queryStudents } from './api.js';
-import { usePagination } from '@alova/scene-react';
-
-const App = () => {
- // search condition status
- const [studentName, setStudentName] = useState('');
- const [clsName, setClsName] = useState('');
- const {
- //...
- } = usePagination(
- (page, pageSize) => queryStudents(page, pageSize, studentName, clsName),
- {
- //...
- // highlight-start
- watchingStates: [studentName, clsName]
- // highlight-end
- }
- );
-
- return (
- // highlight-start
- setStudentName(target.value)} />
-
- // highlight-end
- //...
- );
-};
-```
-
-
-
-
-```html
-
-
-
-
-
-
-
-```
-
-
-
-
-Same as `useWatcher`, you can also implement request debounce by specifying `debounce`, for details, please refer to [useWatcher's debounce parameter setting](/api/core-hooks#usewatcher).
-
-```javascript
-usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), {
- //...
- // highlight-start
- debounce: 300 // Anti-shake parameters, in milliseconds, can also be set as an array to set the anti-shake time separately for watchingStates
- // highlight-end
-});
-```
-
-It should be noted that `debounce` is achieved by request debounce in [**useWatcher**](/api/core-hooks#usewatcher). **At the end of the monitoring state, there are two hidden monitoring states of page and pageSize, which can also be set by debounce. **
-
-For example, when `watchingStates` is set to `[studentName, clsName]`, `[studentName, clsName, page, pageSize]` will be monitored internally, so if you need to set anti-shake for page and pageSize, you can specify ` [0, 0, 500, 500]`.
-
-### Close initialization request
-
-By default, `usePagination` will initiate a request during initialization, but you can also use `immediate` to turn it off, and then pass the `send` function, or change `page` or `pageSize`, and `watchingStates`, etc. state to initiate the request.
-
-```javascript
-usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), {
- //...
- // highlight-start
- immediate: false
- // highlight-end
-});
-```
-
-## List action functions
-
-usePagination provides a fully functional list action function, which can achieve the same effect as the re-request list without re-requesting the list, which greatly improves the interactive experience of the page. The specific function description continues to look down!
-
-### Insert list item
-
-You can use it to insert list items to any position in the list, and it will remove the last item after insertion to ensure the same effect as re-requesting the current page data.
-
-```typescript
-/**
- * Insert data
- * If no index is passed in, it will be inserted at the front by default
- * If a list item is passed in, it will be inserted after the list item, and an error will be thrown if the list item is not in the list data
- * @param item insert item
- * @param indexOrItem insertion position (index)
- */
-declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void;
-```
-
-The following is an example of returning to the first page and then inserting list items in **non-append mode** (page number flipping scenario):
-
-```javascript
-page.value = 1;
-nextTick(() => {
- insert(newItem, 0);
-});
-```
-
-The following is an example of scrolling to the top after inserting a list item in **append mode** (drop-down loading scene):
-
-```javascript
-insert(newItem, 0);
-nextTick(() => {
- window.scrollTo(0, {});
-});
-```
-
-You can also specify the second parameter of `insert` as a list item. When the same reference of this list item is found, the insert item will be inserted after this list item.
-
-```javascript
-insert(newItem, afterItem);
-```
-
-:::warning Caution
-
-In order to make the data correct, the insert function call will clear all caches.
-
-:::
-
-### Remove list item
-
-In the case that the next page has a cache, it will use the cache of the next page to add to the end of the list item after removing an item, so as to ensure the same effect as re-requesting the data of the current page. In **append mode** and Behave the same in **non-append mode**.
-
-```typescript
-/**
- * Remove data
- * If a list item is passed in, the list item will be removed, and an error will be thrown if the list item is not in the list data
- * @param position index or list item to remove
- */
-declare function remove(position: number | LD[number]): void;
-```
-
-You can also specify the second parameter of `remove` as a list item, and when the same reference of this list item is found, this list item will be removed.
-
-But in the following two cases, it will re-initiate the request to refresh the data of the corresponding page:
-
-1. The next page is not cached
-2. The data that exceeds the next page cache list item is continuously called synchronously, and the cache data is not enough to supplement the current page list.
-
-:::warning Caution
-
-In order to make the data correct, the remove function call will clear all caches.
-
-:::
-
-### Update data items
-
-Use this function when you want to update list items.
-
-```typescript
-/**
- * Replace data
- * If the position passed in is a list item, this list item will be replaced, if the list item is not in the list data, an error will be thrown
- * @param item replacement item
- * @param position replacement position (index) or list item
- */
-declare function replace(item: LD extends any[] ? LD[number] : any, position: number | LD[number]): void;
-```
-
-You can also specify the second parameter of `replace` as a list item, which will be replaced when an identical reference to the list item is found.
-
-### Refresh the data of the specified page
-
-When you do not want to update the list items locally after the data operation, but re-request the data on the server side, you can use refresh to refresh the data on any page, without resetting the list data and letting the user start browsing from the first page again.
-
-```typescript
-/**
- * Refresh the specified page number data, this function will ignore the cache to force the send request
- * If no page number is passed in, the current page will be refreshed
- * If a list item is passed in, the page where the list item is located will be refreshed
- * @param pageOrItemPage Refreshed page number or list item
- */
-declare function refresh(pageOrItemPage?: number | LD[number]): void;
-```
-
-In append mode, you can specify the parameter of `refresh` as a list item. When the same reference of this list item is found, the data of the page number where this list item is located will be refreshed.
-
-### Manually update list data
-
-Use the `update` function to update responsive data, which is similar to [useRequest's update](/tutorial/combine-framework/use-request), the only difference is that when calling `update` to update `data`, the list data is updated, while non-response data. This is useful when manually clearing list data without reissuing the request.
-
-```typescript
-// case list data
-update({
- data: []
-});
-```
-
-### Reset list
-
-It will clear all caches and reload the first page.
-
-```typescript
-/**
- * Reload the list from the first page and clear the cache
- */
-declare function reload(): void;
-```
-
-## Limitation
-
-**Cache placeholder mode** are temporarily disabled.
-
-## API
-
-### Hook configuration
-
-Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher).
-
-| Name | Description | Type | Default | Version |
-| ------------------- | --------------------------------------------------------------------- | ------------------------- | -------------------------- | ------- |
-| initialPage | initial page number | number | 1 | - |
-| initialPageSize | Initial number of data items per page | number | 10 | - |
-| watchingStates | state monitoring trigger request, implemented using useWatcher | any[] | [page, pageSize] | - |
-| debounce | The debounce parameter of state monitoring, implemented by useWatcher | number \| number[] | - | - |
-| append | Whether to enable append mode | boolean | false | - |
-| data | Array data specifying pagination | (response: any) => any[] | response => response.data | - |
-| total | specify the total amount of data | (response: any) => number | response => response.total | - |
-| preloadPreviousPage | whether to preload previous page data | boolean | true | - |
-| preloadNextPage | whether to preload next page data | boolean | true | - |
-
-### Responsive data
-
-Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher).
-
-| Name | Description | Type | Version |
-| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- |
-| page | Current page number, determined by initialPage | number | - |
-| pageSize | The current number of pages, determined by initialPageSize | number | - |
-| data | paging list array data, obtained from data configuration | any[] | - |
-| total | The total amount of data, obtained from total configuration, can be empty | number | - |
-| pageCount | The total number of pages, calculated from total and pageSize | number | - |
-| isLastPage | Whether the current page is the last page, if pageCount has a value, it will be obtained by comparing pageCount and page, otherwise it will be obtained by whether the length of the list data is less than pagSize | number | - |
-| fetching | whether data is being preloaded | boolean | - |
-
-### Action function
-
-Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher).
-
-| name | description | function parameters | return value | version |
-| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------- |
-| refresh | Refresh the data of the specified page number, this function will ignore the forced sending request of the cache, in the append mode, the list item can be passed in to indicate the page number where the list item is refreshed | pageOrItemPage: Refreshed page number or list item | - | - |
-| insert | Insert a piece of data. If no index is passed in, it will be inserted at the front by default. If a list item is passed in, it will be inserted after the list item. If the list item is not in the list data, an error will be thrown | 1. item: insert item 2. indexOrItem: insert position (index) or list item, default is 0 | - | - |
-| remove | Remove a piece of data. When a number is passed in, it means the removed index. When the position is passed in a list item, the list item will be removed. If the list item is not in the list data, an error will be thrown | position : remove position (index) or list item | - | - |
-| replace | Replace a piece of data. When the second parameter is passed in a number, it means the replacement index. A negative number means counting from the end. When the position passed in is a list item, the list item will be replaced. If the list item is not in the list data An error will be thrown | 1. item: replacement item 2. position: replacement position (index) or list item, when a negative number is passed in, it means counting from the end | - | - |
-| reload | Clear the data and re-request the first page of data | - | - | - |
-| update | Update state data, same as alova's use hook, but update list data when updating data field | newFrontStates: new state data object | - | - |
-
-### Event
-
-Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher).
-
-| Name | Description | Callback Parameters | Version |
-| --------------- | ---------------------------------------------- | ------------------------------------ | ------- |
-| onFetchSuccess | fetch success callback binding function | event: alova success event object | - |
-| onFetchError | callback binding function for fetch failure | event: alova failure event object | - |
-| onFetchComplete | callback binding function for fetch completion | event: alova completion event object | - |
+---
+title: Pagination request strategy
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info stragety type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with basic usage of alova.
+
+A hook designed for paging scenarios, which can help you automatically manage paging data, preload data, reduce unnecessary data refresh, improve fluency by 300%, and reduce coding difficulty by 50%\*\*. You can use it in the two paging scenarios of pull-down loading and page number flipping. This hook provides a wealth of features to help your application create better performance and more convenient paging functions.
+
+## Example
+
+[page list](/tutorial/example/paginated-list)
+
+[Pull down to load more](/tutorial/example/load-more)
+
+## Features
+
+- Rich and comprehensive paging status;
+- Rich and comprehensive pagination events;
+- Change page, pageSize to automatically get specified paging data;
+- Data caching, no need to repeatedly request list data of the same parameters;
+- Front and back pages are preloaded, no waiting for page turning;
+- Search condition monitoring automatically reacquires pages;
+- Support adding, editing and deleting list data;
+- Support refreshing the data of the specified page without reset;
+- Request-level search anti-shake, no need to maintain by yourself;
+
+## Install
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## Usage
+
+### Display list data
+
+
+
+
+```html
+
+
+ {{ item.name }}
+
+
+
+
+ There are {{ pageCount }} pages
+ A total of {{ total }} data
+
+
+
+```
+
+
+
+
+```jsx
+import { queryStudents } from './api.js';
+import { usePagination } from '@alova/scene-react';
+
+const App = () => {
+ const {
+ // loading state
+ loading,
+
+ // list data
+ data,
+
+ // is it the last page
+ // This parameter can be used to determine whether it needs to be loaded during pull-down loading
+ isLastPage,
+
+ // The current page number, changing this page number will automatically trigger the request
+ page: [page, setPage],
+
+ // Number of data items per page
+ pageSize: [page, setPageSize],
+
+ // number of paging pages
+ pageCount,
+
+ // total amount of data
+ total
+ } = usePagination(
+ // Method instance acquisition function, it will receive page and pageSize, and return a Method instance
+ (page, pageSize) => queryStudents(page, pageSize),
+ {
+ // Initial data before the request (data format returned by the interface)
+ initialData: {
+ total: 0,
+ data: []
+ },
+ initialPage: 1, // initial page number, default is 1
+ initialPageSize: 10 // The initial number of data items per page, the default is 10
+ }
+ );
+
+ // Turn to the previous page, the request will be sent automatically after the page value changes
+ const handlePrevPage = () => {
+ setPage(value => value - 1);
+ };
+
+ // Turn to the next page, the request will be sent automatically after the page value changes
+ const handleNextPage = () => {
+ setPage(value => value + 1);
+ };
+
+ // Change the number of pages, the request will be sent automatically after the pageSize value is changed
+ const handleSetPageSize = () => {
+ setPageSize(20);
+ };
+
+ return (
+
+ {data.map(item => (
+
+ {item.name}
+
+ ))}
+
+
+
+ There are {pageCount} pages
+ A total of {total} pieces of data
+
+{/each}
+
+
+
+There are {pageCount} pages
+A total of {total} pieces of data
+```
+
+
+
+
+### Specify pagination data
+
+The data structure returned by each paging data interface is different, so we need to tell `usePagination` the list data and the total number of items separately, so as to help us manage the paging data.
+
+Suppose the data format returned by your paging interface is as follows:
+
+```typescript
+interface PaginationData {
+ totalNumber: number;
+ list: any[];
+}
+```
+
+At this point, you need to return the list data and the total number of items in the form of a function.
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize), {
+ //...
+ // highlight-start
+ total: response => response.totalNumber,
+ data: response => response.list
+ // highlight-end
+});
+```
+
+If you don't specify the total and data callback functions, they will get data in the following ways by default.
+
+```javascript
+const total = response => response.total;
+const data = response => response.data;
+```
+
+:::warning Caution
+
+The data callback function must return a list of data, indicating the data set used in paging, and total is mainly used to calculate the current page number. If no number is returned in the total callback function, it will pass whether the number of requested lists is less than the pageSize value To determine whether the current page is the last page, which is generally used for pull-down loading.
+
+:::
+
+### Enable append mode
+
+By default, the original list data will be replaced when the page is turned, and the append mode will append the data of the next page to the bottom of the current list when the page is turned. A common usage scenario is to pull down to load more.
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize), {
+ //...
+ // highlight-start
+ append: true
+ // highlight-end
+});
+```
+
+### Preload adjacent page data
+
+In order to provide a better experience for pagination, when the previous and next pages of the current page meet the conditions, it will be automatically preloaded, so that when the user turns the page, the data can be displayed directly without waiting. This is the default behavior. If you don't want to preload the data of adjacent pages, you can turn it off in the following way.
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize), {
+ //...
+ // highlight-start
+ preloadPreviousPage: false, // turn off preloading previous page data
+ preloadNextPage: false // turn off preloading next page data
+ // highlight-end
+});
+```
+
+:::warning preloading trigger conditions
+
+When preloading switch is turned on, the next page will not be loaded blindly. The following two conditions need to be met:
+
+1. Preloading is based on cache of alova. The method instance used for pagination must enable chache. By default, get requests will have 5 minutes of memory cache. If it is a non-get request or the cache is turned off globally, you also need to set the cache in this Method. In the instance, set `localCache` separately to enable cache.
+2. Based on the `total` and `pageSize` parameters, it is determined that there is still data on the next page.
+
+:::
+
+In addition to `onSuccess, onError, onComplete` request events, when preloading is triggered, you can also know the preloading status through `fetching`, and you can also listen to preloading request events through `onFetchSuccess, onFetchError, onFetchComplete`.
+
+```javascript
+const {
+ // preload state
+ fetching,
+
+ // preload success event binding function
+ onFetchSuccess,
+
+ // preload error event binding function
+ onFetchError,
+
+ // Preloading complete event binding function
+ onFetchComplete
+} = usePagination((page, pageSize) => queryStudents(page, pageSize), {
+ //...
+});
+```
+
+### Listening filter conditions
+
+In many cases, the list needs to be filtered by conditions. At this time, the re-request can be triggered through the status monitoring of `usePagination`, which is the same as `useWatcher` provided by alova.
+
+For example, filter by student name, student grade.
+
+
+
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+
+
+
+```jsx
+import { queryStudents } from './api.js';
+import { usePagination } from '@alova/scene-react';
+
+const App = () => {
+ // search condition status
+ const [studentName, setStudentName] = useState('');
+ const [clsName, setClsName] = useState('');
+ const {
+ //...
+ } = usePagination(
+ (page, pageSize) => queryStudents(page, pageSize, studentName, clsName),
+ {
+ //...
+ // highlight-start
+ watchingStates: [studentName, clsName]
+ // highlight-end
+ }
+ );
+
+ return (
+ // highlight-start
+ setStudentName(target.value)} />
+
+ // highlight-end
+ //...
+ );
+};
+```
+
+
+
+
+```html
+
+
+
+
+
+
+
+```
+
+
+
+
+Same as `useWatcher`, you can also implement request debounce by specifying `debounce`, for details, please refer to [useWatcher's debounce parameter setting](/api/core-hooks#usewatcher).
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), {
+ //...
+ // highlight-start
+ debounce: 300 // Anti-shake parameters, in milliseconds, can also be set as an array to set the anti-shake time separately for watchingStates
+ // highlight-end
+});
+```
+
+It should be noted that `debounce` is achieved by request debounce in [**useWatcher**](/api/core-hooks#usewatcher). **At the end of the monitoring state, there are two hidden monitoring states of page and pageSize, which can also be set by debounce. **
+
+For example, when `watchingStates` is set to `[studentName, clsName]`, `[studentName, clsName, page, pageSize]` will be monitored internally, so if you need to set anti-shake for page and pageSize, you can specify ` [0, 0, 500, 500]`.
+
+### Close initialization request
+
+By default, `usePagination` will initiate a request during initialization, but you can also use `immediate` to turn it off, and then pass the `send` function, or change `page` or `pageSize`, and `watchingStates`, etc. state to initiate the request.
+
+```javascript
+usePagination((page, pageSize) => queryStudents(page, pageSize, studentName, clsName), {
+ //...
+ // highlight-start
+ immediate: false
+ // highlight-end
+});
+```
+
+## List action functions
+
+usePagination provides a fully functional list action function, which can achieve the same effect as the re-request list without re-requesting the list, which greatly improves the interactive experience of the page. The specific function description continues to look down!
+
+### Insert list item
+
+You can use it to insert list items to any position in the list, and it will remove the last item after insertion to ensure the same effect as re-requesting the current page data.
+
+```typescript
+/**
+ * Insert data
+ * If no index is passed in, it will be inserted at the front by default
+ * If a list item is passed in, it will be inserted after the list item, and an error will be thrown if the list item is not in the list data
+ * @param item insert item
+ * @param indexOrItem insertion position (index)
+ */
+declare function insert(item: LD[number], indexOrItem?: number | LD[number]): void;
+```
+
+The following is an example of returning to the first page and then inserting list items in **non-append mode** (page number flipping scenario):
+
+```javascript
+page.value = 1;
+nextTick(() => {
+ insert(newItem, 0);
+});
+```
+
+The following is an example of scrolling to the top after inserting a list item in **append mode** (drop-down loading scene):
+
+```javascript
+insert(newItem, 0);
+nextTick(() => {
+ window.scrollTo(0, {});
+});
+```
+
+You can also specify the second parameter of `insert` as a list item. When the same reference of this list item is found, the insert item will be inserted after this list item.
+
+```javascript
+insert(newItem, afterItem);
+```
+
+:::warning Caution
+
+In order to make the data correct, the insert function call will clear all caches.
+
+:::
+
+### Remove list item
+
+In the case that the next page has a cache, it will use the cache of the next page to add to the end of the list item after removing an item, so as to ensure the same effect as re-requesting the data of the current page. In **append mode** and Behave the same in **non-append mode**.
+
+```typescript
+/**
+ * Remove data
+ * If a list item is passed in, the list item will be removed, and an error will be thrown if the list item is not in the list data
+ * @param position index or list item to remove
+ */
+declare function remove(position: number | LD[number]): void;
+```
+
+You can also specify the second parameter of `remove` as a list item, and when the same reference of this list item is found, this list item will be removed.
+
+But in the following two cases, it will re-initiate the request to refresh the data of the corresponding page:
+
+1. The next page is not cached
+2. The data that exceeds the next page cache list item is continuously called synchronously, and the cache data is not enough to supplement the current page list.
+
+:::warning Caution
+
+In order to make the data correct, the remove function call will clear all caches.
+
+:::
+
+### Update data items
+
+Use this function when you want to update list items.
+
+```typescript
+/**
+ * Replace data
+ * If the position passed in is a list item, this list item will be replaced, if the list item is not in the list data, an error will be thrown
+ * @param item replacement item
+ * @param position replacement position (index) or list item
+ */
+declare function replace(
+ item: LD extends any[] ? LD[number] : any,
+ position: number | LD[number]
+): void;
+```
+
+You can also specify the second parameter of `replace` as a list item, which will be replaced when an identical reference to the list item is found.
+
+### Refresh the data of the specified page
+
+When you do not want to update the list items locally after the data operation, but re-request the data on the server side, you can use refresh to refresh the data on any page, without resetting the list data and letting the user start browsing from the first page again.
+
+```typescript
+/**
+ * Refresh the specified page number data, this function will ignore the cache to force the send request
+ * If no page number is passed in, the current page will be refreshed
+ * If a list item is passed in, the page where the list item is located will be refreshed
+ * @param pageOrItemPage Refreshed page number or list item
+ */
+declare function refresh(pageOrItemPage?: number | LD[number]): void;
+```
+
+In append mode, you can specify the parameter of `refresh` as a list item. When the same reference of this list item is found, the data of the page number where this list item is located will be refreshed.
+
+### Manually update list data
+
+Use the `update` function to update responsive data, which is similar to [useRequest's update](/tutorial/combine-framework/use-request), the only difference is that when calling `update` to update `data`, the list data is updated, while non-response data. This is useful when manually clearing list data without reissuing the request.
+
+```typescript
+// case list data
+update({
+ data: []
+});
+```
+
+### Reset list
+
+It will clear all caches and reload the first page.
+
+```typescript
+/**
+ * Reload the list from the first page and clear the cache
+ */
+declare function reload(): void;
+```
+
+## Limitation
+
+**Cache placeholder mode** are temporarily disabled.
+
+## API
+
+### Hook configuration
+
+Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher).
+
+| Name | Description | Type | Default | Version |
+| ------------------- | --------------------------------------------------------------------- | ------------------------- | -------------------------- | ------- |
+| initialPage | initial page number | number | 1 | - |
+| initialPageSize | Initial number of data items per page | number | 10 | - |
+| watchingStates | state monitoring trigger request, implemented using useWatcher | any[] | [page, pageSize] | - |
+| debounce | The debounce parameter of state monitoring, implemented by useWatcher | number \| number[] | - | - |
+| append | Whether to enable append mode | boolean | false | - |
+| data | Array data specifying pagination | (response: any) => any[] | response => response.data | - |
+| total | specify the total amount of data | (response: any) => number | response => response.total | - |
+| preloadPreviousPage | whether to preload previous page data | boolean | true | - |
+| preloadNextPage | whether to preload next page data | boolean | true | - |
+
+### Responsive data
+
+Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher).
+
+| Name | Description | Type | Version |
+| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------- |
+| page | Current page number, determined by initialPage | number | - |
+| pageSize | The current number of pages, determined by initialPageSize | number | - |
+| data | paging list array data, obtained from data configuration | any[] | - |
+| total | The total amount of data, obtained from total configuration, can be empty | number | - |
+| pageCount | The total number of pages, calculated from total and pageSize | number | - |
+| isLastPage | Whether the current page is the last page, if pageCount has a value, it will be obtained by comparing pageCount and page, otherwise it will be obtained by whether the length of the list data is less than pagSize | number | - |
+| fetching | whether data is being preloaded | boolean | - |
+
+### Action function
+
+Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher).
+
+| name | description | function parameters | return value | version |
+| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ------- |
+| refresh | Refresh the data of the specified page number, this function will ignore the forced sending request of the cache, in the append mode, the list item can be passed in to indicate the page number where the list item is refreshed | pageOrItemPage: Refreshed page number or list item | - | - |
+| insert | Insert a piece of data. If no index is passed in, it will be inserted at the front by default. If a list item is passed in, it will be inserted after the list item. If the list item is not in the list data, an error will be thrown | 1. item: insert item 2. indexOrItem: insert position (index) or list item, default is 0 | - | - |
+| remove | Remove a piece of data. When a number is passed in, it means the removed index. When the position is passed in a list item, the list item will be removed. If the list item is not in the list data, an error will be thrown | position : remove position (index) or list item | - | - |
+| replace | Replace a piece of data. When the second parameter is passed in a number, it means the replacement index. A negative number means counting from the end. When the position passed in is a list item, the list item will be replaced. If the list item is not in the list data An error will be thrown | 1. item: replacement item 2. position: replacement position (index) or list item, when a negative number is passed in, it means counting from the end | - | - |
+| reload | Clear the data and re-request the first page of data | - | - | - |
+| update | Update state data, same as alova's use hook, but update list data when updating data field | newFrontStates: new state data object | - | - |
+
+### Event
+
+Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher).
+
+| Name | Description | Callback Parameters | Version |
+| --------------- | ---------------------------------------------- | ------------------------------------ | ------- |
+| onFetchSuccess | fetch success callback binding function | event: alova success event object | - |
+| onFetchError | callback binding function for fetch failure | event: alova failure event object | - |
+| onFetchComplete | callback binding function for fetch completion | event: alova completion event object | - |
diff --git a/docs/tutorial/05-strategy/03-useForm.md b/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md
similarity index 96%
rename from docs/tutorial/05-strategy/03-useForm.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md
index ccf08e333..f858a7516 100644
--- a/docs/tutorial/05-strategy/03-useForm.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/03-useForm.md
@@ -1,525 +1,524 @@
----
-title: Form submit strategy
-sidebar_position: 30
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info strategy type
-
-use hook
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic usage of alova.
-
-A hook designed for form submission. Through this hook, you can easily implement form drafts and multi-page (multi-step) forms. In addition, it also provides common functions such as form reset.
-
-## Example
-
-[Form Submission Demo](/tutorial/example/form-hook)
-
-## Features
-
-- ✨ draft form;
-- ✨ Multi-page (multi-step) forms;
-- ✨ Form submission automatically resets data;
-- ✨Reset form data manually;
-
-## Install
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-## Usage
-
-### Basic usage
-
-Demonstrates basic use of form hooks.
-
-
-
-
-```html
-
-
-
-
-
-
-
-```
-
-
-
-
-```jsx
-import { formSubmit } from './api.js';
-import { useForm } from '@alova/scene-react';
-
-const App = () => {
- const {
- // submit status
- loading: submitting,
-
- // Responsive form data, the content is determined by initialForm
- form,
-
- // submit data function
- send: submit,
-
- // update form item
- updateForm,
-
- // Submit successful callback binding
- onSuccess,
-
- // Submit failure callback binding
- onError,
-
- // Submit completed callback binding
- onComplete
- } = useForm(
- formData => {
- // Form data can be converted and submitted here
- return formSubmit(formData);
- },
- {
- // Initialize form data
- initialForm: {
- name: '',
- cls: '1'
- }
- }
- );
-
- // submit form data
- const handleSubmit = () => {
- // Validate form data...
- submit();
- };
-
- return (
-
- updateForm({ name: target.value })}
- />
-
-
-
- );
-};
-```
-
-
-
-
-```html
-
-
-
-
-
-```
-
-
-
-
-`useForm` will not request by default, and the request will be sent after calling `send`. At the same time, the callback function of `useForm` will pass in the latest form data. If you need to convert the data before submitting, you can convert it here, or Can be converted in the `formSubmit` function.
-
-:::warning Caution
-
-1. `initialForm` is to set the initial form data, `initialData` is to set the initial response data, pay attention to the distinction;
-2. `updateForm` is to update the form data, and `update` is to update the response data, pay attention to the distinction;
-
-:::
-
-The above example only shows a simple form submission function, there is no difference between ordinary form submissions, but `useForm` also implements more practical functions, let us continue to look down.
-
-### Submit auto reset form
-
-Many times, we need to reset the form data after the form is submitted. We always need to manually reassign values one by one when implementing it ourselves, and `useForm` can help us do it automatically.
-
-```javascript
-useForm(submitData, {
- //...
- // highlight-start
- // Set this parameter to true to automatically reset the form data after submission
- resetAfterSubmitting: true
- // highlight-end
-});
-```
-
-If you need to manually reset the form data, you can also do it by calling the `reset` function.
-
-```javascript
-const {
- // highlight-start
- // form reset function
- reset
- // highlight-end
-} = useForm(submitData, {
- //...
-});
-
-// highlight-start
-const handleReset = () => {
- reset();
-};
-// highlight-end
-```
-
-### Update form data
-
-When editing a form, we need to display the data of the original form. At this time, we can use `updateForm` to asynchronously update the form data.
-
-```javascript
-const {
- // ...
- updateForm
-} = useForm(submitData, {
- // ...
- {
- //Initialize form data
- initialForm: {
- name: '',
- cls: '1'
- }
- }
-});
-
-// Request form data and update it to the form
-const { onSuccess } = useRequest(getData);
-onSuccess(({ data }) => {
- updateForm({
- name: data.name,
- cls: data.cls
- });
-});
-```
-
-### Form draft
-
-`useForm` also provides a form draft function, even if the page is refreshed before the data is reset, the form data can be restored. The principle is to use the storage adapter on the alova instance to persist the form data. You only need to set `store` to true when using it.
-
-```javascript
-useForm(submitData, {
- //...
- // highlight-start
- // Turn on persistence to save data. After setting to true, uncommitted data will be persisted in real time
- store: true
- // highlight-end
-});
-```
-
-Before the data is persisted, `JSON.stringify` will be called to convert it into a JSON string. By default, the form data will be serialized when it is persisted. `useForm` has built-in `Date` and `RegExp` instances , which will be useful when using timepickers.
-
-In the form data only involves `Date` and `RegEYou don’t need to do more for xp` instances, but if there are other non-JSON data, such as `moment` instances, we need to customize the serializer, but don’t worry, the custom serializer is very simple, the following will show the settings A `moment` serializer.
-
-```javascript
-import moment from 'moment';
-const momentSerializer = {
- // forward is called when serializing
- // Need to judge whether it is a moment instance, if it is not the target value, return undefined, indicating that it will not be processed
- forward: data => moment.isMoment(data) ? data.valueOf() : undefined,
-
- // backward is called during deserialization, data is the value returned in forward
- backward: timestamp => moment(timestamp);
-};
-
-useForm(
- submitData,
- {
- store: {
- enable: true,
- serializers: {
- moment: momentSerializer
- }
- }
- }
-);
-```
-
-### Multi-page/multi-step forms
-
-Many times we encounter situations where form items are divided into multiple pages, or filled in multiple steps, and submitted in a unified manner at the end, such as multi-step user registration, questionnaire filling, etc., and forms with multiple steps may have interdependence Relationship, if realized by itself will bring some trouble. And `useForm` realizes form data sharing, you can get the same form data in different pages or components, which solves the problem of multi-step form data dependence, and does not need to summarize form data when submitting, and can submit directly.
-
-When using, you need to set the id through `useForm`, and you can share the same form data between different pages with the same id. For example, we have a form that needs to go through 3 steps to fill out the form, and they will go through component A, component B, and component C respectively.
-
-```
-Component A -> Component B -> Component C
-```
-
-At this point, we can initialize the form data inside component A:
-
-```javascript title=Component A
-const returnStates = useForm(submitData, {
- initialForm: {
- step1Input: '',
- step2Input: '',
- step3Input: ''
- },
- // highlight-start
- id: 'testForm'
- // highlight-end
-});
-const { form, send } = returnStates;
-```
-
-In component B and component C, you can directly pass in the id in the first parameter to get the shared data in component A.
-
-```javascript title=Component B, Component C
-const returnStates = useForm('testForm');
-const { form, send } = returnStates;
-```
-
-The `returnStates` returned by id in components B and C are the same reference as the `returnStates` in component A. You can use the same `form`, or you can call `send` in any component to submit the form data uniformly.
-
-**additional**
-
-When obtaining shared data by directly specifying the id in components B and C, the id must first initialize the form data, just like in component A, otherwise `the form data of id {1} is not initial` will be thrown mistake. If your multi-step form is not in a certain order, but in random order according to certain conditions, for example:
-
-```bash
-# possible order 1
-Component B -> Component A -> Component C
-
-# possible order 2
-Component A -> Component C -> Component B
-
-# possible order 3
-Component C -> Component A -> Component B
-
-#...
-```
-
-In this case, you can use `useForm` in component B, C like component A.
-
-```javascript title=Component B, Component C
-const returnStates = useForm(submitData, {
- initialForm: {
- step1Input: '',
- step2Input: '',
- step3Input: ''
- },
- id: 'testForm'
-});
-```
-
-In this way, no matter which component is rendered first, the form with the id of testForm can be initialized, and the subsequent components will first use the initialized form data when encountering the id of testForm, and will not initialize again. This way you can initialize form data inside any component.
-
-> More detailed multi-step forms can also be experienced and viewed in [Form Submission Demo](/tutorial/example/form-hook).
-
-### Conditional filter
-
-`useForm` can also be used in the filtering form used in data filtering scenarios, for example, if you want to search city information by city name, you can set `immediate=true`, it will start querying data at initialization, and then In the operation, call `send` to repeatedly query the data.
-
-```javascript
-const { send: searchData } = useForm(queryCity, {
- initialForm: {
- cityName: ''
- },
- immediate: true
-});
-```
-
-:::warning Conditional Restrictions
-
-In conditional filtering scenarios, `useForm` is more suitable for non-paginated list conditional queries. If you need to perform conditional queries in paginated lists, it is recommended to use [Pagination Request Strategy (usePagination)](/tutorial/strategy/usePagination).
-
-:::
-
-## API
-
-### Hook configuration
-
-Inherit all configurations from [**useRequest**](/api/core-hooks#userequest).
-
-| Name | Description | Type | Default | Version |
-| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------- | ------- |
-| initialForm | initial form data | any | - | - |
-| id | form id, the data data of the same id is the same reference, which can be used to share the same form data in multi-page forms. Single page form does not need to specify id | string \| number | - | - |
-| store | Whether to save data persistently, after setting to true, uncommitted data will be persisted in real time | boolean \| [StoreDetailConfig](#storedetailconfig) \| false | - |
-| resetAfterSubmiting | reset data after submission | boolean | false | - |
-
-### Responsive data
-
-Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest).
-
-| Name | Description | Type | Version |
-| ---- | ----------------------------------- | ---- | ------- |
-| form | form data,determined by initialForm | any | - |
-
-#### StoreDetailConfig
-
-| Name | Description | Type | Default | Version |
-| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ------- |
-| enable | Whether to enable persistent data | boolean | required | - |
-| serializers | A collection of custom serializers, built-in serializers: 1. The date serializer is used to convert dates 2. The regexp serializer is used to convert regular expressions Yes Override the built-in serializer by setting the serializer with the same name | Record\ | - | - |
-
-#### DataSerializer
-
-| Name | Description | Type | Default | Versionthis |
-| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ----------- |
-| forward | Serialization function, when serializing in forward, it needs to judge whether it is the specified data, and return the converted data, otherwise return undefined or not return | (data: any) => any \| undefined \| void | required | - |
-| backward | deserialization function, deserialization data directly | (data: any) => any \| undefined \| void | required | - |
-
-### Action function
-
-Inherit all action functions of [**useRequest**](/api/core-hooks#userequest).
-
-| name | description | function parameters | return value | version |
-| ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------ | ------- |
-| updateForm | Update one or more form data | newForm: Partial\ \| (oldForm: F) => F) F is `initialForm` type | - | - |
-| reset | Reset to initialized data, if there is persistent data, it will also be cleared | - | - | - |
-
-### Event
-
-Inherit all events from [**useRequest**](/api/core-hooks#userequest).
-
-| Name | Description | Callback Parameters | Version |
-| --------- | ----------------------------------------------- | ------------------- | ------- |
-| onRestore | Triggered after the persistent data is restored | - | - |
+---
+title: Form submit strategy
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+A hook designed for form submission. Through this hook, you can easily implement form drafts and multi-page (multi-step) forms. In addition, it also provides common functions such as form reset.
+
+## Example
+
+[Form Submission Demo](/tutorial/example/form-hook)
+
+## Features
+
+- draft form;
+- Multi-page (multi-step) forms;
+- Form submission automatically resets data;
+- Reset form data manually;
+
+## Install
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## Usage
+
+### Basic usage
+
+Demonstrates basic use of form hooks.
+
+
+
+
+```html
+
+
+
+
+
+
+
+```
+
+
+
+
+```jsx
+import { formSubmit } from './api.js';
+import { useForm } from '@alova/scene-react';
+
+const App = () => {
+ const {
+ // submit status
+ loading: submitting,
+
+ // Responsive form data, the content is determined by initialForm
+ form,
+
+ // submit data function
+ send: submit,
+
+ // update form item
+ updateForm,
+
+ // Submit successful callback binding
+ onSuccess,
+
+ // Submit failure callback binding
+ onError,
+
+ // Submit completed callback binding
+ onComplete
+ } = useForm(
+ formData => {
+ // Form data can be converted and submitted here
+ return formSubmit(formData);
+ },
+ {
+ // Initialize form data
+ initialForm: {
+ name: '',
+ cls: '1'
+ }
+ }
+ );
+
+ // submit form data
+ const handleSubmit = () => {
+ // Validate form data...
+ submit();
+ };
+
+ return (
+
+ updateForm({ name: target.value })}
+ />
+
+
+
+ );
+};
+```
+
+
+
+
+```html
+
+
+
+
+
+```
+
+
+
+
+`useForm` will not request by default, and the request will be sent after calling `send`. At the same time, the callback function of `useForm` will pass in the latest form data. If you need to convert the data before submitting, you can convert it here, or Can be converted in the `formSubmit` function.
+
+:::warning Caution
+
+1. `initialForm` is to set the initial form data, `initialData` is to set the initial response data, pay attention to the distinction;
+2. `updateForm` is to update the form data, and `update` is to update the response data, pay attention to the distinction;
+
+:::
+
+The above example only shows a simple form submission function, there is no difference between ordinary form submissions, but `useForm` also implements more practical functions, let us continue to look down.
+
+### Submit auto reset form
+
+Many times, we need to reset the form data after the form is submitted. We always need to manually reassign values one by one when implementing it ourselves, and `useForm` can help us do it automatically.
+
+```javascript
+useForm(submitData, {
+ //...
+ // highlight-start
+ // Set this parameter to true to automatically reset the form data after submission
+ resetAfterSubmitting: true
+ // highlight-end
+});
+```
+
+If you need to manually reset the form data, you can also do it by calling the `reset` function.
+
+```javascript
+const {
+ // highlight-start
+ // form reset function
+ reset
+ // highlight-end
+} = useForm(submitData, {
+ //...
+});
+
+// highlight-start
+const handleReset = () => {
+ reset();
+};
+// highlight-end
+```
+
+### Update form data
+
+When editing a form, we need to display the data of the original form. At this time, we can use `updateForm` to asynchronously update the form data.
+
+```javascript
+const {
+ // ...
+ updateForm
+} = useForm(submitData, {
+ // ...
+ {
+ //Initialize form data
+ initialForm: {
+ name: '',
+ cls: '1'
+ }
+ }
+});
+
+// Request form data and update it to the form
+const { onSuccess } = useRequest(getData);
+onSuccess(({ data }) => {
+ updateForm({
+ name: data.name,
+ cls: data.cls
+ });
+});
+```
+
+### Form draft
+
+`useForm` also provides a form draft function, even if the page is refreshed before the data is reset, the form data can be restored. The principle is to use the storage adapter on the alova instance to persist the form data. You only need to set `store` to true when using it.
+
+```javascript
+useForm(submitData, {
+ //...
+ // highlight-start
+ // Turn on persistence to save data. After setting to true, uncommitted data will be persisted in real time
+ store: true
+ // highlight-end
+});
+```
+
+Before the data is persisted, `JSON.stringify` will be called to convert it into a JSON string. By default, the form data will be serialized when it is persisted. `useForm` has built-in `Date` and `RegExp` instances , which will be useful when using timepickers.
+
+In the form data only involves `Date` and `RegEYou don’t need to do more for xp` instances, but if there are other non-JSON data, such as `moment` instances, we need to customize the serializer, but don’t worry, the custom serializer is very simple, the following will show the settings A `moment` serializer.
+
+```javascript
+import moment from 'moment';
+const momentSerializer = {
+ // forward is called when serializing
+ // Need to judge whether it is a moment instance, if it is not the target value, return undefined, indicating that it will not be processed
+ forward: data => moment.isMoment(data) ? data.valueOf() : undefined,
+
+ // backward is called during deserialization, data is the value returned in forward
+ backward: timestamp => moment(timestamp);
+};
+
+useForm(
+ submitData,
+ {
+ store: {
+ enable: true,
+ serializers: {
+ moment: momentSerializer
+ }
+ }
+ }
+);
+```
+
+### Multi-page/multi-step forms
+
+Many times we encounter situations where form items are divided into multiple pages, or filled in multiple steps, and submitted in a unified manner at the end, such as multi-step user registration, questionnaire filling, etc., and forms with multiple steps may have interdependence Relationship, if realized by itself will bring some trouble. And `useForm` realizes form data sharing, you can get the same form data in different pages or components, which solves the problem of multi-step form data dependence, and does not need to summarize form data when submitting, and can submit directly.
+
+When using, you need to set the id through `useForm`, and you can share the same form data between different pages with the same id. For example, we have a form that needs to go through 3 steps to fill out the form, and they will go through component A, component B, and component C respectively.
+
+```
+Component A -> Component B -> Component C
+```
+
+At this point, we can initialize the form data inside component A:
+
+```javascript title=Component A
+const returnStates = useForm(submitData, {
+ initialForm: {
+ step1Input: '',
+ step2Input: '',
+ step3Input: ''
+ },
+ // highlight-start
+ id: 'testForm'
+ // highlight-end
+});
+const { form, send } = returnStates;
+```
+
+In component B and component C, you can directly pass in the id in the first parameter to get the shared data in component A.
+
+```javascript title=Component B, Component C
+const returnStates = useForm('testForm');
+const { form, send } = returnStates;
+```
+
+The `returnStates` returned by id in components B and C are the same reference as the `returnStates` in component A. You can use the same `form`, or you can call `send` in any component to submit the form data uniformly.
+
+**additional**
+
+When obtaining shared data by directly specifying the id in components B and C, the id must first initialize the form data, just like in component A, otherwise `the form data of id {1} is not initial` will be thrown mistake. If your multi-step form is not in a certain order, but in random order according to certain conditions, for example:
+
+```bash
+# possible order 1
+Component B -> Component A -> Component C
+
+# possible order 2
+Component A -> Component C -> Component B
+
+# possible order 3
+Component C -> Component A -> Component B
+
+#...
+```
+
+In this case, you can use `useForm` in component B, C like component A.
+
+```javascript title=Component B, Component C
+const returnStates = useForm(submitData, {
+ initialForm: {
+ step1Input: '',
+ step2Input: '',
+ step3Input: ''
+ },
+ id: 'testForm'
+});
+```
+
+In this way, no matter which component is rendered first, the form with the id of testForm can be initialized, and the subsequent components will first use the initialized form data when encountering the id of testForm, and will not initialize again. This way you can initialize form data inside any component.
+
+> More detailed multi-step forms can also be experienced and viewed in [Form Submission Demo](/tutorial/example/form-hook).
+
+### Conditional filter
+
+`useForm` can also be used in the filtering form used in data filtering scenarios, for example, if you want to search city information by city name, you can set `immediate=true`, it will start querying data at initialization, and then In the operation, call `send` to repeatedly query the data.
+
+```javascript
+const { send: searchData } = useForm(queryCity, {
+ initialForm: {
+ cityName: ''
+ },
+ immediate: true
+});
+```
+
+:::warning Conditional Restrictions
+
+In conditional filtering scenarios, `useForm` is more suitable for non-paginated list conditional queries. If you need to perform conditional queries in paginated lists, it is recommended to use [Pagination Request Strategy (usePagination)](/tutorial/strategy/usePagination).
+
+:::
+
+## API
+
+### Hook configuration
+
+Inherit all configurations from [**useRequest**](/api/core-hooks#userequest).
+
+| Name | Description | Type | Default | Version |
+| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------- | ------- |
+| initialForm | initial form data | any | - | - |
+| id | form id, the data data of the same id is the same reference, which can be used to share the same form data in multi-page forms. Single page form does not need to specify id | string \| number | - | - |
+| store | Whether to save data persistently, after setting to true, uncommitted data will be persisted in real time | boolean \| [StoreDetailConfig](#storedetailconfig) \| false | - |
+| resetAfterSubmiting | reset data after submission | boolean | false | - |
+
+### Responsive data
+
+Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest).
+
+| Name | Description | Type | Version |
+| ---- | ----------------------------------- | ---- | ------- |
+| form | form data,determined by initialForm | any | - |
+
+#### StoreDetailConfig
+
+| Name | Description | Type | Default | Version |
+| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------- | ------- |
+| enable | Whether to enable persistent data | boolean | required | - |
+| serializers | A collection of custom serializers, built-in serializers: 1. The date serializer is used to convert dates 2. The regexp serializer is used to convert regular expressions Yes Override the built-in serializer by setting the serializer with the same name | Record\ | - | - |
+
+#### DataSerializer
+
+| Name | Description | Type | Default | Versionthis |
+| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------- | ----------- |
+| forward | Serialization function, when serializing in forward, it needs to judge whether it is the specified data, and return the converted data, otherwise return undefined or not return | (data: any) => any \| undefined \| void | required | - |
+| backward | deserialization function, deserialization data directly | (data: any) => any \| undefined \| void | required | - |
+
+### Action function
+
+Inherit all action functions of [**useRequest**](/api/core-hooks#userequest).
+
+| name | description | function parameters | return value | version |
+| ---------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------ | ------- |
+| updateForm | Update one or more form data | newForm: Partial\ \| (oldForm: F) => F) F is `initialForm` type | - | - |
+| reset | Reset to initialized data, if there is persistent data, it will also be cleared | - | - | - |
+
+### Event
+
+Inherit all events from [**useRequest**](/api/core-hooks#userequest).
+
+| Name | Description | Callback Parameters | Version |
+| --------- | ----------------------------------------------- | ------------------- | ------- |
+| onRestore | Triggered after the persistent data is restored | - | - |
diff --git a/docs/tutorial/05-strategy/04-tokenAuthentication.md b/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md
similarity index 91%
rename from docs/tutorial/05-strategy/04-tokenAuthentication.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md
index 48ff0e403..893e6afe3 100644
--- a/docs/tutorial/05-strategy/04-tokenAuthentication.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/04-tokenAuthentication.md
@@ -1,588 +1,582 @@
----
-title: Token authentication interceptor
-sidebar_position: 40
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info
-
-Policy type: Interceptor
-
-Version requirements: v1.3.0+
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic use of alova.
-
-Token authentication interceptor provides unified management of token-based login, logout, token assignment, and token refresh, and supports silent token refresh.
-
-## Features
-
-- ✨ Unified maintenance of all codes for token identity authentication, including login, logout, token assignment, token refresh, etc.;
-- ✨ Supports verification of token expiration on the client and server, and refreshes the token without any warning;
-- ✨ Requests that rely on tokens automatically wait for the token refresh to complete before requesting;
-- ✨ Set request ID with metadata;
-- ✨ Automatically release visitor requests that do not rely on tokens;
-
-## Install
-
-
-
-
-```bash
-#npm
-npm install @alova/scene-vue --save
-# yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-#npm
-npm install @alova/scene-react --save
-# yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-#npm
-npm install @alova/scene-svelte --save
-# yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-:::info
-
-All the following interceptors are optional, just choose the ones you want to use.
-
-:::
-
-## Bind Token authentication interceptor
-
-Token identity authentication is completed through global interceptors, which provide `createClientTokenAuthentication` and `createServerTokenAuthentication` for client- and server-based identity authentication respectively.
-
-- Client-based identity authentication: means judging whether the token has expired from the client, such as the token expiration time obtained during login;
-- Server-based identity authentication: It indicates whether the token has expired based on the status returned from the server. For example, when `status` is 401, it means it has expired;
-
-### Bind client-based authentication interceptor
-
-```javascript
-import { createClientTokenAuthentication } from '@alova/scene-*';
-import { createAlova } from 'alova';
-
-const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({
- // ...
-});
-
-const alovaInstance = createAlova({
- // ...
- beforeRequest: onAuthRequired(method => {
- // ...interceptor before original request
- }),
- responded: onResponseRefreshToken((response, method) => {
- //...original response success interceptor
- return response.json();
- })
-});
-```
-
-In `onResponseRefreshToken`, you can also bind response error and completion interceptors, which is the same as the original usage.
-
-```javascript
-createAlova({
- // ...
- // highlight-start
- responded: onResponseRefreshToken({
- onSuccess: (response, method) => {
- //...original response success interceptor
- },
- onError: (error, method) => {
- //...original response error interceptor
- },
- onComplete: method => {
- //...original response completion interceptor
- }
- })
- // highlight-end
-});
-```
-
-If you don't need to set an interceptor, you don't need to pass in the interceptor function.
-
-```javascript
-createAlova({
- //...
- // highlight-start
- beforeRequest: onAuthRequired(),
- responded: onResponseRefreshToken()
- // highlight-end
-});
-```
-
-### Bind server-based authentication interceptor
-
-Same as client-based usage
-
-```javascript
-import { createServerTokenAuthentication } from '@alova/scene-*';
-import { createAlova } from 'alova';
-
-const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({
- // ...
-});
-
-const alovaInstance = createAlova({
- // ...
- beforeRequest: onAuthRequired(method => {
- // ...interceptor before original request
- }),
- responded: onResponseRefreshToken((response, method) => {
- //...original response success interceptor
- return response.json();
- })
-});
-```
-
-:::warning
-
-When you use the `GlobalFetch` adapter, you may encounter the problem `TypeError: Failed to execute 'json' on 'Response': body stream already read`. This is because the `body stream` of `Response` can only be accessed once, you can `response.clone().json()` to solve it.
-
-:::
-
-## Refresh Token silently on the client
-
-Just set `refreshToken` and specify whether the token expires, and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete.
-
-```javascript
-createClientTokenAuthentication({
- refreshToken: {
- // Triggered before the request, the method parameter will be received and a boolean will be returned to indicate whether the token has expired.
- isExpired: method => {
- return tokenExpireTime < Date.now();
- },
-
- // Triggered when the token expires, trigger the refresh token in this function
- handler: async method => {
- try {
- const { token, refresh_token } = await refreshToken();
- localStorage.setItem('token', token);
- localStorage.setItem('refresh_token', refresh_token);
- } catch (error) {
- // redirect to login page once token refresh failed.
- location.href = '/login';
- // and must throw error.
- throw error;
- }
- }
- }
-});
-```
-
-:::warning Attention
-
-1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata.
-2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request.
-
-:::
-
-> For more information about metadata, go to [method metadata](/tutorial/getting-started/method-metadata).
-
-```javascript
-export const refreshToken = () => {
- const method = alovaInstance.Get('/refresh_token');
- method.meta = {
- authRole: 'refreshToken'
- };
- return method;
-};
-```
-
-## Refresh Token silently on the server side
-
-The same as refreshing the token silently on the client, just specify whether the token expires and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete.
-
-### Processed in request success interceptor
-
-When using `GlobalFetch`, as long as the server returns the response data, the response success interceptor will be triggered. At this time, we need to handle the token refresh in the response success interceptor.
-
-```javascript
-createServerTokenAuthentication({
- refreshTokenOnSuccess: {
- // Triggered when responding, the response and method can be obtained, and a boolean is returned to indicate whether the token has expired.
- // When the server returns 401, it means the token has expired.
- isExpired: (response, method) => {
- return response.status === 401;
- },
-
- // Triggered when the token expires, trigger the refresh token in this function
- handler: async (response, method) => {
- try {
- const { token, refresh_token } = await refreshToken();
- localStorage.setItem('token', token);
- localStorage.setItem('refresh_token', refresh_token);
- } catch (error) {
- // redirect to login page once token refresh failed.
- location.href = '/login';
- // and must throw error.
- throw error;
- }
- }
- }
-});
-```
-
-:::warning Special attention
-
-1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata.
-2. If the token refresh fails, an error must be thrown to prevent the failed interface from retrying and the waiting interface from continuing the request.
-
-:::
-
-### Handled in request error interceptor
-
-When using the `axios` interceptor, if the server returns a status code other than `200/300`, the response error interceptor will be triggered. At this time, we need to handle the token refresh in the response error interceptor.
-
-```javascript
-createServerTokenAuthentication({
- refreshTokenOnError: {
- // Triggered when responding, error and method can be obtained, and boolean is returned to indicate whether the token has expired.
- // When the server returns 401, it means the token has expired.
- isExpired: (error, method) => {
- return error.response.status === 401;
- },
-
- // Triggered when the token expires, trigger the refresh token in this function
- handler: async (error, method) => {
- try {
- const { token, refresh_token } = await refreshToken();
- localStorage.setItem('token', token);
- localStorage.setItem('refresh_token', refresh_token);
- } catch (error) {
- // redirect to login page once token refresh failed.
- location.href = '/login';
- // and must throw error.
- throw error;
- }
- }
- }
-});
-```
-
-:::warning Attention
-
-1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata.
-2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request.
-
-:::
-
-> For more information about metadata, please go to [method metadata](/tutorial/getting-started/method-metadata).
-
-```javascript
-export const refreshToken = () => {
- const method = alovaInstance.Get('/refresh_token');
- method.meta = {
- authRole: 'refreshToken'
- };
- return method;
-};
-```
-
-## Release visitor request
-
-Some interfaces do not need to rely on token authentication. We call them "guest requests". At this time, we can set their metadata to `authRole: null` to bypass the front-end interception and allow them to send requests and receive responses smoothly.
-
-```javascript
-export const requestTokenNotRequired = () => {
- const method = alovaInstance.Get('/token_not_required');
- method.meta = {
- authRole: null
- };
- return method;
-};
-```
-
-## Login interception
-
-In the identity authentication interceptor, you can also intercept login requests and save login information in the interceptor to achieve the purpose of unified maintenance of identity authentication codes.
-
-First identify the metadata of the login request as `authRole: 'login'`.
-
-```javascript
-export const login = () => {
- const method = alovaInstance.Get('/login');
- method.meta = {
- authRole: 'login'
- };
- return method;
-};
-```
-
-Then save the login information in the login interceptor.
-
-```javascript
-createClientTokenAuthentication({
- login(response, method) {
- localStorage.setItem('token', response.token);
- localStorage.setItem('refresh_token', response.refresh_token);
- }
-});
-```
-
-> The login interceptor usage of `createServerTokenAuthentication` is the same.
-
-## Assign token
-
-Usually, we will append token to the request information in `beforeRequest`. The `assignToken` callback function is provided in the Token authentication interceptor for assigning tokens. It will filter guest requests and login requests and trigger them before the requests. It can also achieve the purpose of unified maintenance of identity authentication codes.
-
-```javascript
-createClientTokenAuthentication({
- assignToken: method => {
- method.config.headers.Authorization = localStorage.getItem('token')};
- }
-});
-```
-
-> The usage of assignToken callback function of `createServerTokenAuthentication` is the same.
-
-## Logout interception
-
-When your logout also requires calling the interface, you can also intercept the logout request and clear the login information.
-
-First identify the logout request metadata as `authRole: 'logout'`.
-
-```javascript
-export const logout = () => {
- const method = alovaInstance.Get('/logout');
- method.meta = {
- authRole: 'logout'
- };
- return method;
-};
-```
-
-Then clear the login information in the logout interceptor.
-
-```javascript
-createClientTokenAuthentication({
- logout(response, method) {
- localStorage.removeItem('token');
- localStorage.removeItem('refresh_token');
- }
-});
-```
-
-> The login interceptor usage of `createServerTokenAuthentication` is the same.
-
-## Custom identification identity
-
-The above metadata identities are actually the default identities. If you need to customize the identity, you can set it as follows.
-
-### token refresh identity
-
-```javascript
-createClientTokenAuthentication({
- refreshToken: {
- // highlight-start
- metaMatches: {
- refreshToken: true
- }
- // highlight-end
- // ...
- }
-});
-```
-
-```javascript
-createServerTokenAuthentication({
- refreshTokenOnSuccess: {
- // highlight-start
- metaMatches: {
- refreshToken: true
- }
- // highlight-end
- // ...
- },
- refreshTokenOnError: {
- // highlight-start
- metaMatches: {
- refreshToken: true
- }
- // highlight-end
- // ...
- }
-});
-```
-
-Then, requests with `refreshToken: true` in the metadata will be identified as `refreshToken`.
-
-```javascript
-export const refreshToken = () => {
- const method = alovaInstance.Get('/refresh_token');
- method.meta = {
- refreshToken: true
- };
- return method;
-};
-```
-
-### Visitor identify
-
-```javascript
-createClientTokenAuthentication({
- visitorMeta: {
- isVisitor: true
- }
-});
-```
-
-Then, requests with `isVisitor: true` in the metadata will be identified as visitors.
-
-```javascript
-export const requestTokenNotRequired = () => {
- const method = alovaInstance.Get('/token_not_required');
- method.meta = {
- isVisitor: true
- };
- return method;
-};
-```
-
-### Login identity
-
-```javascript
-createClientTokenAuthentication({
- login: {
- // highlight-start
- metaMatches: {
- login: true
- },
- // highlight-end
- handler(response, method) {
- //Login interceptor
- }
- }
-});
-```
-
-Then, requests with `login: true` in the metadata will be identified as `login` identity.
-
-```javascript
-export const login = () => {
- const method = alovaInstance.Get('/login');
- method.meta = {
- login: true
- };
- return method;
-};
-```
-
-### Log out identity
-
-```javascript
-createClientTokenAuthentication({
- logout: {
- // highlight-start
- metaMatches: {
- logout: true
- },
- // highlight-end
- handler(response, method) {
- //Logout interceptor
- }
- }
-});
-```
-
-Then, requests with `logout: true` in the metadata will be identified as `logout`.
-
-```javascript
-export const logout = () => {
- const method = alovaInstance.Get('/logout');
- method.meta = {
- logout: true
- };
- return method;
-};
-```
-
-> The login interceptor usage of `createServerTokenAuthentication` is the same.
-
-## Typescript
-
-By default, `createClientServerTokenAuthentication` and `createServerTokenAuthentication` are adapted to the `GlobalFetch` request adapter, you can only specify the type of `StatesHook`, as follows:
-
-```typescript
-// highlight-start
-const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({
- // highlight-end
- //...
-});
-
-const alovaInstance = createAlova({
- // ...
- beforeRequest: onAuthRequired(method => {
- // The type of method is Method
- }),
- responded: onResponseRefreshToken((response, method) => {
- //The response type is Response
- return response.json();
- })
-});
-```
-
-If you are not using the `GlobalFetch` request adapter, You also need to specify the type of request adapter, which is also simple.
-
-The following is an example of the axios request adapter. Specify the request adapter type in `createClientTokenAuthentication`.
-
-```typescript
-import { axiosRequestAdapter } from '@alova/adapter-axios';
-
-// highlight-start
-const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication<
- typeof ReactHook,
- typeof axiosRequestAdapter
->({
- // highlight-end
- //...
-});
-const alovaInstance = createAlova({
- //...
- // highlight-start
- beforeRequest: onAuthRequired(method => {
- // The type of method is Method
- // highlight-end
- }),
- // highlight-start
- responded: onResponseRefreshToken((response, method) => {
- //The response type is AxiosResponse
- // highlight-end
- return response.data;
- })
-});
-```
-
-The server-based Token authentication interceptor is used in the same way.
-
-```typescript
-import { axiosRequestAdapter } from '@alova/adapter-axios';
-
-// highlight-start
-createServerTokenAuthentication({
- // highlight-end
- //...
-});
-```
+---
+title: Token authentication interceptor
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info
+
+Policy type: Interceptor
+
+Version requirements: v1.3.0+
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic use of alova.
+
+Token authentication interceptor provides unified management of token-based login, logout, token assignment, and token refresh, and supports silent token refresh.
+
+## Features
+
+- Unified maintenance of all codes for token identity authentication, including login, logout, token assignment, token refresh, etc.;
+- Supports verification of token expiration on the client and server, and refreshes the token without any warning;
+- Requests that rely on tokens automatically wait for the token refresh to complete before requesting;
+- Set request ID with metadata;
+- Automatically release visitor requests that do not rely on tokens;
+
+## Install
+
+
+
+
+```bash
+#npm
+npm install @alova/scene-vue --save
+# yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+#npm
+npm install @alova/scene-react --save
+# yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+#npm
+npm install @alova/scene-svelte --save
+# yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+:::info
+
+All the following interceptors are optional, just choose the ones you want to use.
+
+:::
+
+## Bind Token authentication interceptor
+
+Token identity authentication is completed through global interceptors, which provide `createClientTokenAuthentication` and `createServerTokenAuthentication` for client- and server-based identity authentication respectively.
+
+- Client-based identity authentication: means judging whether the token has expired from the client, such as the token expiration time obtained during login;
+- Server-based identity authentication: It indicates whether the token has expired based on the status returned from the server. For example, when `status` is 401, it means it has expired;
+
+### Bind client-based authentication interceptor
+
+```javascript
+import { createClientTokenAuthentication } from '@alova/scene-*';
+import { createAlova } from 'alova';
+
+const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication({
+ // ...
+});
+
+const alovaInstance = createAlova({
+ // ...
+ beforeRequest: onAuthRequired(method => {
+ // ...interceptor before original request
+ }),
+ responded: onResponseRefreshToken((response, method) => {
+ //...original response success interceptor
+ return response.json();
+ })
+});
+```
+
+In `onResponseRefreshToken`, you can also bind response error and completion interceptors, which is the same as the original usage.
+
+```javascript
+createAlova({
+ // ...
+ // highlight-start
+ responded: onResponseRefreshToken({
+ onSuccess: (response, method) => {
+ //...original response success interceptor
+ },
+ onError: (error, method) => {
+ //...original response error interceptor
+ },
+ onComplete: method => {
+ //...original response completion interceptor
+ }
+ })
+ // highlight-end
+});
+```
+
+If you don't need to set an interceptor, you don't need to pass in the interceptor function.
+
+```javascript
+createAlova({
+ //...
+ // highlight-start
+ beforeRequest: onAuthRequired(),
+ responded: onResponseRefreshToken()
+ // highlight-end
+});
+```
+
+### Bind server-based authentication interceptor
+
+Same as client-based usage
+
+```javascript
+import { createServerTokenAuthentication } from '@alova/scene-*';
+import { createAlova } from 'alova';
+
+const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication({
+ // ...
+});
+
+const alovaInstance = createAlova({
+ // ...
+ beforeRequest: onAuthRequired(method => {
+ // ...interceptor before original request
+ }),
+ responded: onResponseRefreshToken((response, method) => {
+ //...original response success interceptor
+ return response.json();
+ })
+});
+```
+
+:::warning
+
+When you use the `GlobalFetch` adapter, you may encounter the problem `TypeError: Failed to execute 'json' on 'Response': body stream already read`. This is because the `body stream` of `Response` can only be accessed once, you can `response.clone().json()` to solve it.
+
+:::
+
+## Refresh Token silently on the client
+
+Just set `refreshToken` and specify whether the token expires, and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete.
+
+```javascript
+createClientTokenAuthentication({
+ refreshToken: {
+ // Triggered before the request, the method parameter will be received and a boolean will be returned to indicate whether the token has expired.
+ isExpired: method => {
+ return tokenExpireTime < Date.now();
+ },
+
+ // Triggered when the token expires, trigger the refresh token in this function
+ handler: async method => {
+ try {
+ const { token, refresh_token } = await refreshToken();
+ localStorage.setItem('token', token);
+ localStorage.setItem('refresh_token', refresh_token);
+ } catch (error) {
+ // redirect to login page once token refresh failed.
+ location.href = '/login';
+ // and must throw error.
+ throw error;
+ }
+ }
+ }
+});
+```
+
+:::warning Attention
+
+1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata.
+2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request.
+
+:::
+
+> For more information about metadata, go to [method metadata](/tutorial/getting-started/method-metadata).
+
+```javascript
+export const refreshToken = () => {
+ const method = alovaInstance.Get('/refresh_token');
+ method.meta = {
+ authRole: 'refreshToken'
+ };
+ return method;
+};
+```
+
+## Refresh Token silently on the server side
+
+The same as refreshing the token silently on the client, just specify whether the token expires and call the function to refresh the token. When the token refresh is completed, all requests that rely on the token will wait for the token refresh to complete.
+
+### Processed in request success interceptor
+
+When using `GlobalFetch`, as long as the server returns the response data, the response success interceptor will be triggered. At this time, we need to handle the token refresh in the response success interceptor.
+
+```javascript
+createServerTokenAuthentication({
+ refreshTokenOnSuccess: {
+ // Triggered when responding, the response and method can be obtained, and a boolean is returned to indicate whether the token has expired.
+ // When the server returns 401, it means the token has expired.
+ isExpired: (response, method) => {
+ return response.status === 401;
+ },
+
+ // Triggered when the token expires, trigger the refresh token in this function
+ handler: async (response, method) => {
+ try {
+ const { token, refresh_token } = await refreshToken();
+ localStorage.setItem('token', token);
+ localStorage.setItem('refresh_token', refresh_token);
+ } catch (error) {
+ // redirect to login page once token refresh failed.
+ location.href = '/login';
+ // and must throw error.
+ throw error;
+ }
+ }
+ }
+});
+```
+
+### Handled in request error interceptor
+
+When using the `axios` interceptor, if the server returns a status code other than `200/300`, the response error interceptor will be triggered. At this time, we need to handle the token refresh in the response error interceptor.
+
+```javascript
+createServerTokenAuthentication({
+ refreshTokenOnError: {
+ // Triggered when responding, error and method can be obtained, and boolean is returned to indicate whether the token has expired.
+ // When the server returns 401, it means the token has expired.
+ isExpired: (error, method) => {
+ return error.response.status === 401;
+ },
+
+ // Triggered when the token expires, trigger the refresh token in this function
+ handler: async (error, method) => {
+ try {
+ const { token, refresh_token } = await refreshToken();
+ localStorage.setItem('token', token);
+ localStorage.setItem('refresh_token', refresh_token);
+ } catch (error) {
+ // redirect to login page once token refresh failed.
+ location.href = '/login';
+ // and must throw error.
+ throw error;
+ }
+ }
+ }
+});
+```
+
+:::warning Attention
+
+1. In order for the `refreshToken` request to pass smoothly, the `authRole` needs to be marked as `refreshToken` through metadata.
+2. If the token refresh fails, an error must be thrown to prevent the failed API from retrying and the waiting APIs from continuing the request.
+
+:::
+
+> For more information about metadata, please go to [method metadata](/tutorial/getting-started/method-metadata).
+
+```javascript
+export const refreshToken = () => {
+ const method = alovaInstance.Get('/refresh_token');
+ method.meta = {
+ authRole: 'refreshToken'
+ };
+ return method;
+};
+```
+
+## Release visitor request
+
+Some interfaces do not need to rely on token authentication. We call them "guest requests". At this time, we can set their metadata to `authRole: null` to bypass the front-end interception and allow them to send requests and receive responses smoothly.
+
+```javascript
+export const requestTokenNotRequired = () => {
+ const method = alovaInstance.Get('/token_not_required');
+ method.meta = {
+ authRole: null
+ };
+ return method;
+};
+```
+
+## Login interception
+
+In the identity authentication interceptor, you can also intercept login requests and save login information in the interceptor to achieve the purpose of unified maintenance of identity authentication codes.
+
+First identify the metadata of the login request as `authRole: 'login'`.
+
+```javascript
+export const login = () => {
+ const method = alovaInstance.Get('/login');
+ method.meta = {
+ authRole: 'login'
+ };
+ return method;
+};
+```
+
+Then save the login information in the login interceptor.
+
+```javascript
+createClientTokenAuthentication({
+ login(response, method) {
+ localStorage.setItem('token', response.token);
+ localStorage.setItem('refresh_token', response.refresh_token);
+ }
+});
+```
+
+> The login interceptor usage of `createServerTokenAuthentication` is the same.
+
+## Assign token
+
+Usually, we will append token to the request information in `beforeRequest`. The `assignToken` callback function is provided in the Token authentication interceptor for assigning tokens. It will filter guest requests and login requests and trigger them before the requests. It can also achieve the purpose of unified maintenance of identity authentication codes.
+
+```javascript
+createClientTokenAuthentication({
+ assignToken: method => {
+ method.config.headers.Authorization = localStorage.getItem('token')};
+ }
+});
+```
+
+> The usage of assignToken callback function of `createServerTokenAuthentication` is the same.
+
+## Logout interception
+
+When your logout also requires calling the interface, you can also intercept the logout request and clear the login information.
+
+First identify the logout request metadata as `authRole: 'logout'`.
+
+```javascript
+export const logout = () => {
+ const method = alovaInstance.Get('/logout');
+ method.meta = {
+ authRole: 'logout'
+ };
+ return method;
+};
+```
+
+Then clear the login information in the logout interceptor.
+
+```javascript
+createClientTokenAuthentication({
+ logout(response, method) {
+ localStorage.removeItem('token');
+ localStorage.removeItem('refresh_token');
+ }
+});
+```
+
+> The login interceptor usage of `createServerTokenAuthentication` is the same.
+
+## Custom identification identity
+
+The above metadata identities are actually the default identities. If you need to customize the identity, you can set it as follows.
+
+### token refresh identity
+
+```javascript
+createClientTokenAuthentication({
+ refreshToken: {
+ // highlight-start
+ metaMatches: {
+ refreshToken: true
+ }
+ // highlight-end
+ // ...
+ }
+});
+```
+
+```javascript
+createServerTokenAuthentication({
+ refreshTokenOnSuccess: {
+ // highlight-start
+ metaMatches: {
+ refreshToken: true
+ }
+ // highlight-end
+ // ...
+ },
+ refreshTokenOnError: {
+ // highlight-start
+ metaMatches: {
+ refreshToken: true
+ }
+ // highlight-end
+ // ...
+ }
+});
+```
+
+Then, requests with `refreshToken: true` in the metadata will be identified as `refreshToken`.
+
+```javascript
+export const refreshToken = () => {
+ const method = alovaInstance.Get('/refresh_token');
+ method.meta = {
+ refreshToken: true
+ };
+ return method;
+};
+```
+
+### Visitor identify
+
+```javascript
+createClientTokenAuthentication({
+ visitorMeta: {
+ isVisitor: true
+ }
+});
+```
+
+Then, requests with `isVisitor: true` in the metadata will be identified as visitors.
+
+```javascript
+export const requestTokenNotRequired = () => {
+ const method = alovaInstance.Get('/token_not_required');
+ method.meta = {
+ isVisitor: true
+ };
+ return method;
+};
+```
+
+### Login identity
+
+```javascript
+createClientTokenAuthentication({
+ login: {
+ // highlight-start
+ metaMatches: {
+ login: true
+ },
+ // highlight-end
+ handler(response, method) {
+ //Login interceptor
+ }
+ }
+});
+```
+
+Then, requests with `login: true` in the metadata will be identified as `login` identity.
+
+```javascript
+export const login = () => {
+ const method = alovaInstance.Get('/login');
+ method.meta = {
+ login: true
+ };
+ return method;
+};
+```
+
+### Log out identity
+
+```javascript
+createClientTokenAuthentication({
+ logout: {
+ // highlight-start
+ metaMatches: {
+ logout: true
+ },
+ // highlight-end
+ handler(response, method) {
+ //Logout interceptor
+ }
+ }
+});
+```
+
+Then, requests with `logout: true` in the metadata will be identified as `logout`.
+
+```javascript
+export const logout = () => {
+ const method = alovaInstance.Get('/logout');
+ method.meta = {
+ logout: true
+ };
+ return method;
+};
+```
+
+> The login interceptor usage of `createServerTokenAuthentication` is the same.
+
+## Typescript
+
+By default, `createClientServerTokenAuthentication` and `createServerTokenAuthentication` are adapted to the `GlobalFetch` request adapter, you can only specify the type of `StatesHook`, as follows:
+
+```typescript
+// highlight-start
+const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication<
+ typeof VueHook
+>({
+ // highlight-end
+ //...
+});
+
+const alovaInstance = createAlova({
+ // ...
+ beforeRequest: onAuthRequired(method => {
+ // The type of method is Method
+ }),
+ responded: onResponseRefreshToken((response, method) => {
+ //The response type is Response
+ return response.json();
+ })
+});
+```
+
+If you are not using the `GlobalFetch` request adapter, You also need to specify the type of request adapter, which is also simple.
+
+The following is an example of the axios request adapter. Specify the request adapter type in `createClientTokenAuthentication`.
+
+```typescript
+import { axiosRequestAdapter } from '@alova/adapter-axios';
+
+// highlight-start
+const { onAuthRequired, onResponseRefreshToken } = createClientTokenAuthentication<
+ typeof ReactHook,
+ typeof axiosRequestAdapter
+>({
+ // highlight-end
+ //...
+});
+const alovaInstance = createAlova({
+ //...
+ // highlight-start
+ beforeRequest: onAuthRequired(method => {
+ // The type of method is Method
+ // highlight-end
+ }),
+ // highlight-start
+ responded: onResponseRefreshToken((response, method) => {
+ //The response type is AxiosResponse
+ // highlight-end
+ return response.data;
+ })
+});
+```
+
+The server-based Token authentication interceptor is used in the same way.
+
+```typescript
+import { axiosRequestAdapter } from '@alova/adapter-axios';
+
+// highlight-start
+createServerTokenAuthentication({
+ // highlight-end
+ //...
+});
+```
diff --git a/docs/tutorial/05-strategy/05-useUploader.md b/versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md
similarity index 67%
rename from docs/tutorial/05-strategy/05-useUploader.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md
index c98e2f027..b1a36b1ba 100644
--- a/docs/tutorial/05-strategy/05-useUploader.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/05-useUploader.md
@@ -1,6 +1,5 @@
----
-title: Universal upload strategy
-sidebar_position: 40
----
-
-coming soon...
+---
+title: Universal upload strategy
+---
+
+coming soon...
diff --git a/docs/tutorial/05-strategy/06-useAutoRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md
similarity index 74%
rename from docs/tutorial/05-strategy/06-useAutoRequest.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md
index 0f457373c..9a69384b3 100644
--- a/docs/tutorial/05-strategy/06-useAutoRequest.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/06-useAutoRequest.md
@@ -1,168 +1,170 @@
----
-title: Automatically refetch data
-sidebar_position: 60
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info strategy type
-
-use hook
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic use of alova.
-
-Automatically fetch data through browser events or polling, allowing the interface to display the newest data.
-
-## Features
-
-- ✨ Supports refetching the newest data in scenarios such as browser focus, tab switching, network reconnection, polling requests, etc;
-- ✨ Supports request throttling, only one request will be sent if triggered multiple times in a short period of time;
-- ✨ Support custom event listening functions to adapt to usage scenarios in non-browser environments;
-
-## Install
-
-
-
-
-```bash
-#npm
-npm install @alova/scene-vue --save
-# yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-#npm
-npm install @alova/scene-react --save
-# yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-#npm
-npm install @alova/scene-svelte --save
-# yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-## Basic usage
-
-By default, useHook`useAutoRequest` that automatically fetches data will automatically fetch the newest data when browser is visible, hidden, focused, and the network is reconnected, and will automatically cancel the listening event when the component is uninstalled.
-
-```javascript
-import { useAutoRequest } from '@alova/scene-*';
-
-const { loading, data, error } = useAutoRequest(() => method());
-```
-
-The return value of `useAutoRequest` is the same as [useRequest](/api/core-hooks#userequest).
-
-In addition to supporting all configuration parameters of [useRequest](/api/core-hooks#userequest), it also supports automatically fetched configuration parameters. You can turn on or off some events through the following configuration, or modify request throttling events.
-
-```javascript
-const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest(() => method(), {
- /**
- * Browser display hide trigger
- * @default true
- */
- enableVisibility: true,
-
- /**
- * Triggered by browser focus
- * @default true
- */
- enableFocus: true,
-
- /**
- * Triggered by network reconnection
- * @default true
- */
- enableNetwork: true,
-
- /**
- *Throttling time, only one request will be sent if triggered multiple times within a certain period, unit ms
- * @default 1000
- */
- throttle: 1000,
-
- /**
- * The time of polling request, effective when set greater than 0, unit ms
- * @default 0
- */
- pollingTime: 2000
-
- //Other parameters are the same as useRequest...
-});
-```
-
-:::warning caching advice
-
-It is recommended to turn off the cache of the corresponding request when using `useAutoRequest`, because when the cache is set, the cache will be hit when the automatic request is triggered and the newest data cannot be obtained. Please read [Cache Mode](/tutorial/cache/mode) for details.
-
-:::
-
-## Custom listening functions
-
-The above 4 methods of automatically fetching data are implemented by listening browser's events by default. When users use it in a non-browser environment, you may need to customize the listening function. This function receives the notification request function and useHook config as parameters, and returns a cancel listening function.
-.
-
-The following is an example of custom listening function in `react-native`:
-
-### Network reconnection custom function
-
-```javascript
-import NetInfo from '@react-native-community/netinfo';
-useAutoRequest.onNetwork = (notify, config) => {
- const unsubscribe = NetInfo.addEventListener(({ isConnected }) => {
- isConnected && notify();
- });
- return unsubscribe;
-};
-```
-
-### Polling custom function
-
-```javascript
-useAutoRequest.onPolling = (notify, config) => {
- const timer = setInterval(notify, config.pollingTime);
- return () => clearInterval(timer);
-};
-```
-
-### App switching custom function
-
-```javascript
-import { AppState, Text } from 'react-native';
-useAutoRequest.onVisibility = (notify, config) => {
- const subscription = AppState.addEventListener('change', state => {
- state === 'active' && notify();
- });
- return () => subscription.remove();
-};
-```
-
-### App focus custom function
-
-Since the App doesn't have a focus event, it can be set to an empty function to avoid throwing error.
-
-```javascript
-useAutoRequest.onFocus = (notify, config) => {
- return () => {};
-};
-```
+---
+title: Automatically refetch data
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic use of alova.
+
+Automatically fetch data through browser events or polling, allowing the interface to display the newest data.
+
+## Features
+
+- Supports refetching the newest data in scenarios such as browser focus, tab switching, network reconnection, polling requests, etc;
+- Supports request throttling, only one request will be sent if triggered multiple times in a short period of time;
+- Support custom event listening functions to adapt to usage scenarios in non-browser environments;
+
+## Install
+
+
+
+
+```bash
+#npm
+npm install @alova/scene-vue --save
+# yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+#npm
+npm install @alova/scene-react --save
+# yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+#npm
+npm install @alova/scene-svelte --save
+# yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## Basic usage
+
+By default, useHook`useAutoRequest` that automatically fetches data will automatically fetch the newest data when browser is visible, hidden, focused, and the network is reconnected, and will automatically cancel the listening event when the component is uninstalled.
+
+```javascript
+import { useAutoRequest } from '@alova/scene-*';
+
+const { loading, data, error } = useAutoRequest(() => method());
+```
+
+The return value of `useAutoRequest` is the same as [useRequest](/api/core-hooks#userequest).
+
+In addition to supporting all configuration parameters of [useRequest](/api/core-hooks#userequest), it also supports automatically fetched configuration parameters. You can turn on or off some events through the following configuration, or modify request throttling events.
+
+```javascript
+const { loading, data, error, onSuccess, onError, onComplete } = useAutoRequest(
+ () => method(),
+ {
+ /**
+ * Browser display hide trigger
+ * @default true
+ */
+ enableVisibility: true,
+
+ /**
+ * Triggered by browser focus
+ * @default true
+ */
+ enableFocus: true,
+
+ /**
+ * Triggered by network reconnection
+ * @default true
+ */
+ enableNetwork: true,
+
+ /**
+ *Throttling time, only one request will be sent if triggered multiple times within a certain period, unit ms
+ * @default 1000
+ */
+ throttle: 1000,
+
+ /**
+ * The time of polling request, effective when set greater than 0, unit ms
+ * @default 0
+ */
+ pollingTime: 2000
+
+ //Other parameters are the same as useRequest...
+ }
+);
+```
+
+:::warning caching advice
+
+It is recommended to turn off the cache of the corresponding request when using `useAutoRequest`, because when the cache is set, the cache will be hit when the automatic request is triggered and the newest data cannot be obtained. Please read [Cache Mode](/tutorial/cache/mode) for details.
+
+:::
+
+## Custom listening functions
+
+The above 4 methods of automatically fetching data are implemented by listening browser's events by default. When users use it in a non-browser environment, you may need to customize the listening function. This function receives the notification request function and useHook config as parameters, and returns a cancel listening function.
+.
+
+The following is an example of custom listening function in `react-native`:
+
+### Network reconnection custom function
+
+```javascript
+import NetInfo from '@react-native-community/netinfo';
+useAutoRequest.onNetwork = (notify, config) => {
+ const unsubscribe = NetInfo.addEventListener(({ isConnected }) => {
+ isConnected && notify();
+ });
+ return unsubscribe;
+};
+```
+
+### Polling custom function
+
+```javascript
+useAutoRequest.onPolling = (notify, config) => {
+ const timer = setInterval(notify, config.pollingTime);
+ return () => clearInterval(timer);
+};
+```
+
+### App switching custom function
+
+```javascript
+import { AppState, Text } from 'react-native';
+useAutoRequest.onVisibility = (notify, config) => {
+ const subscription = AppState.addEventListener('change', state => {
+ state === 'active' && notify();
+ });
+ return () => subscription.remove();
+};
+```
+
+### App focus custom function
+
+Since the App doesn't have a focus event, it can be set to an empty function to avoid throwing error.
+
+```javascript
+useAutoRequest.onFocus = (notify, config) => {
+ return () => {};
+};
+```
diff --git a/docs/tutorial/05-strategy/07-useBreakpointUploader.md b/versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md
similarity index 64%
rename from docs/tutorial/05-strategy/07-useBreakpointUploader.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md
index 8d06ed628..9a7887fa9 100644
--- a/docs/tutorial/05-strategy/07-useBreakpointUploader.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/07-useBreakpointUploader.md
@@ -1,6 +1,5 @@
----
-title: Breakpoint upload
-sidebar_position: 60
----
-
-coming soon...
+---
+title: Breakpoint upload
+---
+
+coming soon...
diff --git a/docs/tutorial/05-strategy/08-useCaptcha.md b/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md
similarity index 90%
rename from docs/tutorial/05-strategy/08-useCaptcha.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md
index 67115c91c..d6cbf71e7 100644
--- a/docs/tutorial/05-strategy/08-useCaptcha.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/08-useCaptcha.md
@@ -1,213 +1,218 @@
----
-title: send captcha
-sidebar_position: 40
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info strategy type
-
-use hook
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic usage of alova.
-
-The verification code sending hook saves you the trouble of developing the verification code sending function.
-
-## Example
-
-[Send Captcha Demo](/tutorial/example/captcha-send)
-
-## Features
-
-- ✨ The countdown will start automatically after the verification code is sent;
-- ✨ Custom countdown seconds;
-- ✨ Verification code sending limit;
-
-## Install
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-## use
-
-## Basic usage
-
-Demonstrates basic use of form hooks.
-
-
-
-
-```html
-
-
-
-
-
-
-```
-
-
-
-
-```jsx
-import { useState } from 'react';
-import { apiSendCaptcha } from './api.js';
-import { useCaptcha } from '@alova/scene-react';
-
-const App = () => {
- const [mobile, setMobile] = ref('');
- const {
- // send status
- loading: sending,
-
- // Call sendCaptcha to request the interface to send the verification code
- send: sendCaptcha
- } = useCaptcha(() => apiSendCaptcha(mobile));
-
- return (
-
- setMobile(target.value)}
- />
-
-
- );
-};
-```
-
-
-
-
-```html
-
-
-
-
-```
-
-
-
-
-By default, after the verification code is successfully sent, it will count down for 60 seconds, and calling `send` when the countdown is not over will throw an error.
-
-### Custom countdown seconds
-
-You can also customize the countdown seconds
-
-```javascript
-useCaptcha(() => apiSendCaptcha(mobile.value), {
- //...
- // highlight-start
- // Set the countdown to 20 seconds
- initialCountdown: 20
- // highlight-end
-});
-```
-
-## API
-
-### Hook configuration
-
-Inherit all configurations of [**useRequest**](/api/core-hooks#userequest) except `immediate`, `immediate` in `useCaptcha` has been hard-coded to false.
-
-| Name | Description | Type | Default | Version |
-| ---------------- | ---------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- |
-| initialCountdown | Initial countdown, when the verification code is sent successfully, it will start countdown with this data | number | 60 | - |
-
-### Responsive data
-
-Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest).
-
-| Name | Description | Type | Version |
-| --------- | ----------------------------------------------------------------------------------------------------------- | ------ | ------- |
-| countdown | The current countdown, -1 per second, the verification code can only be sent again after the countdown ends | number | - |
-
-### Action function
-
-Inherit all action functions of [**useRequest**](/api/core-hooks#userequest).
-
-| name | description | function parameters | return value | version |
-| ---- | ---------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------- |
-| send | Send a request, when the countdown is not over, the call will throw an error | Consistent with useRequest.send | Promise\ | - |
-
-### Event
-
-Inherit all events from [**useRequest**](/api/core-hooks#userequest).
+---
+title: send captcha
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+The verification code sending hook saves you the trouble of developing the verification code sending function.
+
+## Example
+
+[Send Captcha Demo](/tutorial/example/captcha-send)
+
+## Features
+
+- The countdown will start automatically after the verification code is sent;
+- Custom countdown seconds;
+- Verification code sending limit;
+
+## Install
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## use
+
+## Basic usage
+
+Demonstrates basic use of form hooks.
+
+
+
+
+```html
+
+
+
+
+
+
+```
+
+
+
+
+```jsx
+import { useState } from 'react';
+import { apiSendCaptcha } from './api.js';
+import { useCaptcha } from '@alova/scene-react';
+
+const App = () => {
+ const [mobile, setMobile] = ref('');
+ const {
+ // send status
+ loading: sending,
+
+ // Call sendCaptcha to request the interface to send the verification code
+ send: sendCaptcha
+ } = useCaptcha(() => apiSendCaptcha(mobile));
+
+ return (
+
+ setMobile(target.value)}
+ />
+
+
+ );
+};
+```
+
+
+
+
+```html
+
+
+
+
+```
+
+
+
+
+By default, after the verification code is successfully sent, it will count down for 60 seconds, and calling `send` when the countdown is not over will throw an error.
+
+### Custom countdown seconds
+
+You can also customize the countdown seconds
+
+```javascript
+useCaptcha(() => apiSendCaptcha(mobile.value), {
+ //...
+ // highlight-start
+ // Set the countdown to 20 seconds
+ initialCountdown: 20
+ // highlight-end
+});
+```
+
+## API
+
+### Hook configuration
+
+Inherit all configurations of [**useRequest**](/api/core-hooks#userequest) except `immediate`, `immediate` in `useCaptcha` has been hard-coded to false.
+
+| Name | Description | Type | Default | Version |
+| ---------------- | ---------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- |
+| initialCountdown | Initial countdown, when the verification code is sent successfully, it will start countdown with this data | number | 60 | - |
+
+### Responsive data
+
+Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest).
+
+| Name | Description | Type | Version |
+| --------- | ----------------------------------------------------------------------------------------------------------- | ------ | ------- |
+| countdown | The current countdown, -1 per second, the verification code can only be sent again after the countdown ends | number | - |
+
+### Action function
+
+Inherit all action functions of [**useRequest**](/api/core-hooks#userequest).
+
+| name | description | function parameters | return value | version |
+| ---- | ---------------------------------------------------------------------------- | ------------------------------- | ------------------- | ------- |
+| send | Send a request, when the countdown is not over, the call will throw an error | Consistent with useRequest.send | Promise\ | - |
+
+### Event
+
+Inherit all events from [**useRequest**](/api/core-hooks#userequest).
diff --git a/docs/tutorial/05-strategy/09-actionDelegationMiddleware.md b/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md
similarity index 96%
rename from docs/tutorial/05-strategy/09-actionDelegationMiddleware.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md
index 73436b2c2..ad7be7d2f 100644
--- a/docs/tutorial/05-strategy/09-actionDelegationMiddleware.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/09-actionDelegationMiddleware.md
@@ -1,244 +1,243 @@
----
-title: Cross components to trigger request
-sidebar_position: 50
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info strategy type
-
-middleware
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic usage of alova.
-
-In the past, if you want to trigger a request in another component in one component, you need to save the data in the Store and complete it by dispatching Action. Now, you can use this middleware to **remove the limitation of component hierarchy**, and quickly trigger any request action function in any component.
-
-For example, after updating the menu data in a component, you can re-trigger the re-request of the side menu bar to refresh the data. When the list data is manipulated, the list update is triggered.
-
-## Example
-
-[Cross-component trigger request Demo](/tutorial/example/action-delegation-middleware)
-
-## Features
-
-- ✨Delegate the action function of any use hook in alova;
-- ✨ Trigger the delegated action function anywhere;
-
-## Install
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-## use
-
-### Basic usage
-
-> Take vue3 as an example, the usage is the same in react and svelte.
-
-Use `actionDelegationMiddleware` in component A to delegate the action function of `useRequest`.
-
-```javascript title=Component A
-import { actionDelegationMiddleware } from '@alova/scene-vue';
-
-useRequest(queryTodo, {
- //...
- middleware: actionDelegationMiddleware('actionName')
-});
-```
-
-In any component (such as component B), pass in the specified delegate name through `accessAction` to trigger the action function of `useRequest` in component A.
-
-```javascript title=Component B
-import { accessAction } from '@alova/scene-vue';
-
-accessAction('actionName', delegatedActions => {
- // Call the send function in component A
- delegatedActions.send();
-
- // Call the abort function in component A
- delegatedActions.abort();
-});
-```
-
-:::info note
-
-1. All use hooks in alova support action function delegation, but the functions delegated by different use hooks are different.
-2. When using `actionDelegationMiddleware`, the delegate name can be passed in strings, numbers, and symbol values.
-
-:::
-
-### Batch trigger action function
-
-In the above example, we use `accessAction` to trigger the action function of a use hook, but in fact, delegates with the same name will not override each other, but will be stored in a group, and we can use this name to trigger them at the same time The delegated function.
-
-```javascript title=Component C
-import { actionDelegationMiddleware } from '@alova/scene-vue';
-
-useRequest(queryTodo, {
- //...
- middleware: actionDelegationMiddleware('actionName1')
-});
-```
-
-```javascript title=Component D
-import { actionDelegationMiddleware } from '@alova/scene-vue';
-
-useRequest(queryTodo, {
- //...
- middleware: actionDelegationMiddleware('actionName1')
-});
-```
-
-```javascript title=Component E
-import { accessAction } from '@alova/scene-vue';
-
-// Because the delegate hooks of component C and component D will be matched, the callback function will be executed twice
-accessAction('actionName1', delegatedActions => {
- // Call the send function in component C and component D
- delegatedActions.send();
-
- // Call the abort function in component C and component D
- delegatedActions.abort();
-});
-```
-
-At the same time, regular expressions can also be used in `accessAction` to trigger batches of action functions whose delegate names meet the conditions
-
-```javascript title=Component F
-import { actionDelegationMiddleware } from '@alova/scene-vue';
-
-useRequest(queryTodo, {
- //...
- middleware: actionDelegationMiddleware('prefix_name1')
-});
-```
-
-```javascript title=Component G
-import { actionDelegationMiddleware } from '@alova/scene-vue';
-
-useRequest(queryTodo, {
- //...
- middleware: actionDelegationMiddleware('prefix_name2')
-});
-```
-
-```javascript title=Component H
-import { accessAction } from '@alova/scene-vue';
-
-// Because the delegate hooks of component F and component G will be matched, the callback function will be executed twice
-accessAction(/^prefix_/, delegatedActions => {
- // Call the send function in component F and component G
- delegatedActions.send();
-
- // Call the abort function in component F and component G
- delegatedActions.abort();
-});
-```
-
-## Action function delegation list
-
-Although the action functions delegated by most hooks are the same as the action functions themselves, this is not absolute. The following is the action function delegation list of each hook.
-
-### useRequest
-
-| name | description | function parameters | return value | version |
-| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- |
-| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - |
-| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - |
-| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - |
-
-### useWatcher
-
-Same as [useRequest delegate list](#userequest).
-
-### useFetcher
-
-| name | description | function parameters | return value | version |
-| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- |
-| fetch | Same as [useFetcher](/api/core-hooks#usefetcher).fetch | | | - |
-| abort | Same as [useFetcher](/api/core-hooks#usefetcher).abort | | | - |
-| update | Same as [useFetcher](/api/core-hooks#usefetcher).update | | | - |
-
-### usePagination
-
-| name | description | function parameters | return value | version |
-| -------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------ | ------- |
-| refresh | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
-| insert | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
-| remove | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
-| replace | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
-| reload | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
-| update | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
-| getState | Get paging related data by name | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | Corresponding to the value of the statekey | - |
-
-### useSQRequest
-
-Same as [useRequest delegate list](#userequest).
-
-### useForm
-
-| name | description | function parameters | return value | version |
-| ---------- | -------------------------------------------------------------------------- | ------------------- | ------------ | ------- |
-| updateForm | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - |
-| reset | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - |
-| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - |
-| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - |
-| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - |
-
-### useCaptcha
-
-Same as [useRequest delegate list](#userequest).
-
-### useRetriableRequest
-
-| name | description | function parameters | return value | version |
-| ------ | ------------------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- |
-| stop | See [useRetriableRequest action function](/tutorial/strategy/useRetriableRequest#api) for details | | | - |
-| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - |
-| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - |
-| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - |
-
-### useSerialRequest
-
-Same as [useRequest delegate list](#userequest).
-
-### useSerialWatcher
-
-Same as [useRequest delegate list](#userequest).
+---
+title: Cross components to trigger request
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+middleware
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+In the past, if you want to trigger a request in another component in one component, you need to save the data in the Store and complete it by dispatching Action. Now, you can use this middleware to **remove the limitation of component hierarchy**, and quickly trigger any request action function in any component.
+
+For example, after updating the menu data in a component, you can re-trigger the re-request of the side menu bar to refresh the data. When the list data is manipulated, the list update is triggered.
+
+## Example
+
+[Cross-component trigger request Demo](/tutorial/example/action-delegation-middleware)
+
+## Features
+
+- Delegate the action function of any use hook in alova;
+- Trigger the delegated action function anywhere;
+
+## Install
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## use
+
+### Basic usage
+
+> Take vue3 as an example, the usage is the same in react and svelte.
+
+Use `actionDelegationMiddleware` in component A to delegate the action function of `useRequest`.
+
+```javascript title=Component A
+import { actionDelegationMiddleware } from '@alova/scene-vue';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('actionName')
+});
+```
+
+In any component (such as component B), pass in the specified delegate name through `accessAction` to trigger the action function of `useRequest` in component A.
+
+```javascript title=Component B
+import { accessAction } from '@alova/scene-vue';
+
+accessAction('actionName', delegatedActions => {
+ // Call the send function in component A
+ delegatedActions.send();
+
+ // Call the abort function in component A
+ delegatedActions.abort();
+});
+```
+
+:::info note
+
+1. All use hooks in alova support action function delegation, but the functions delegated by different use hooks are different.
+2. When using `actionDelegationMiddleware`, the delegate name can be passed in strings, numbers, and symbol values.
+
+:::
+
+### Batch trigger action function
+
+In the above example, we use `accessAction` to trigger the action function of a use hook, but in fact, delegates with the same name will not override each other, but will be stored in a group, and we can use this name to trigger them at the same time The delegated function.
+
+```javascript title=Component C
+import { actionDelegationMiddleware } from '@alova/scene-vue';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('actionName1')
+});
+```
+
+```javascript title=Component D
+import { actionDelegationMiddleware } from '@alova/scene-vue';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('actionName1')
+});
+```
+
+```javascript title=Component E
+import { accessAction } from '@alova/scene-vue';
+
+// Because the delegate hooks of component C and component D will be matched, the callback function will be executed twice
+accessAction('actionName1', delegatedActions => {
+ // Call the send function in component C and component D
+ delegatedActions.send();
+
+ // Call the abort function in component C and component D
+ delegatedActions.abort();
+});
+```
+
+At the same time, regular expressions can also be used in `accessAction` to trigger batches of action functions whose delegate names meet the conditions
+
+```javascript title=Component F
+import { actionDelegationMiddleware } from '@alova/scene-vue';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('prefix_name1')
+});
+```
+
+```javascript title=Component G
+import { actionDelegationMiddleware } from '@alova/scene-vue';
+
+useRequest(queryTodo, {
+ //...
+ middleware: actionDelegationMiddleware('prefix_name2')
+});
+```
+
+```javascript title=Component H
+import { accessAction } from '@alova/scene-vue';
+
+// Because the delegate hooks of component F and component G will be matched, the callback function will be executed twice
+accessAction(/^prefix_/, delegatedActions => {
+ // Call the send function in component F and component G
+ delegatedActions.send();
+
+ // Call the abort function in component F and component G
+ delegatedActions.abort();
+});
+```
+
+## Action function delegation list
+
+Although the action functions delegated by most hooks are the same as the action functions themselves, this is not absolute. The following is the action function delegation list of each hook.
+
+### useRequest
+
+| name | description | function parameters | return value | version |
+| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- |
+| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - |
+| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - |
+| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - |
+
+### useWatcher
+
+Same as [useRequest delegate list](#userequest).
+
+### useFetcher
+
+| name | description | function parameters | return value | version |
+| ------ | ------------------------------------------------------- | ------------------- | ------------ | ------- |
+| fetch | Same as [useFetcher](/api/core-hooks#usefetcher).fetch | | | - |
+| abort | Same as [useFetcher](/api/core-hooks#usefetcher).abort | | | - |
+| update | Same as [useFetcher](/api/core-hooks#usefetcher).update | | | - |
+
+### usePagination
+
+| name | description | function parameters | return value | version |
+| -------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------ | ------- |
+| refresh | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
+| insert | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
+| remove | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
+| replace | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
+| reload | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
+| update | For details, see [usePagination action function](/tutorial/strategy/usePagination#api) | | | - |
+| getState | Get paging related data by name | stateKey: 'page' \| 'pageSize' \| 'data' \| 'pageCount' \| 'total' \| 'isLastPage' | Corresponding to the value of the statekey | - |
+
+### useSQRequest
+
+Same as [useRequest delegate list](#userequest).
+
+### useForm
+
+| name | description | function parameters | return value | version |
+| ---------- | -------------------------------------------------------------------------- | ------------------- | ------------ | ------- |
+| updateForm | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - |
+| reset | For details, see [useForm action function](/tutorial/strategy/useForm#api) | | | - |
+| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - |
+| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - |
+| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - |
+
+### useCaptcha
+
+Same as [useRequest delegate list](#userequest).
+
+### useRetriableRequest
+
+| name | description | function parameters | return value | version |
+| ------ | ------------------------------------------------------------------------------------------------- | ------------------- | ------------ | ------- |
+| stop | See [useRetriableRequest action function](/tutorial/strategy/useRetriableRequest#api) for details | | | - |
+| send | Same as [useRequset](/api/core-hooks#userequest).send | | | - |
+| abort | Same as [useRequset](/api/core-hooks#userequest).abort | | | - |
+| update | Same as [useRequset](/api/core-hooks#userequest).update | | | - |
+
+### useSerialRequest
+
+Same as [useRequest delegate list](#userequest).
+
+### useSerialWatcher
+
+Same as [useRequest delegate list](#userequest).
diff --git a/docs/tutorial/05-strategy/10-useSerialRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md
similarity index 89%
rename from docs/tutorial/05-strategy/10-useSerialRequest.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md
index 1e5c6e5b8..bc1b1c3db 100644
--- a/docs/tutorial/05-strategy/10-useSerialRequest.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/10-useSerialRequest.md
@@ -1,144 +1,143 @@
----
-title: useRequest with serial
-sidebar_position: 60
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info strategy type
-
-use hook
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic usage of alova.
-
-This use hook is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions.
-
-## Features
-
-- ✨ A more concise and easy-to-use serial method;
-- ✨Unified request status and callback function;
-- ✨ send function can trigger serial execution of multiple requests;
-
-## Example
-
-[serial request](/tutorial/example/serial-request)
-
-## Install
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-## Usage
-
-### Basic usage
-
-Same usage as `useRequest`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request.
-
-```javascript
-const {
- // Serial loading status, all requests will be changed to false
- loading,
-
- // The response data of the last request
- data,
-
- // Any request error will record the error message here
- error,
-
- // Manually send a serial request
- send,
-
- // serial request success callback binding function
- onSuccess,
-
- // Serial request error callback binding function, any request error will trigger it
- onError,
-
- // Serial request completion callback binding function
- onComplete
-} = useSerialRequest(
- [
- // args is the parameter passed in by the send function
- (...args) => request1(args),
-
- // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second
- (response1, ...args) => request2(response1, args),
- (response2, ...args) => request3(response2, args)
- ],
- {
- immediate: false
- }
-);
-
-// emit request manually and pass arguments
-send(1, 2, 3);
-```
-
-It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function.
-
-```javascript
-useSerialRequest([
- methodInstance,
- (response1, ...args) => request2(response1, args),
- (response2, ...args) => request3(response2, args)
-]);
-```
-
-### Request error
-
-When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error.
-
-## API
-
-### Hook configuration
-
-Inherit all configurations from [**useRequest**](/api/core-hooks#userequest).
-
-### Responsive data
-
-Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest).
-
-### Action function
-
-Inherit all action functions of [**useRequest**](/api/core-hooks#userequest).
-
-### Event
-
-Inherit all events from [**useRequest**](/api/core-hooks#userequest).
+---
+title: useRequest with serial
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+This use hook is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions.
+
+## Features
+
+- A more concise and easy-to-use serial method;
+- Unified request status and callback function;
+- send function can trigger serial execution of multiple requests;
+
+## Example
+
+[serial request](/tutorial/example/serial-request)
+
+## Install
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## Usage
+
+### Basic usage
+
+Same usage as `useRequest`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request.
+
+```javascript
+const {
+ // Serial loading status, all requests will be changed to false
+ loading,
+
+ // The response data of the last request
+ data,
+
+ // Any request error will record the error message here
+ error,
+
+ // Manually send a serial request
+ send,
+
+ // serial request success callback binding function
+ onSuccess,
+
+ // Serial request error callback binding function, any request error will trigger it
+ onError,
+
+ // Serial request completion callback binding function
+ onComplete
+} = useSerialRequest(
+ [
+ // args is the parameter passed in by the send function
+ (...args) => request1(args),
+
+ // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second
+ (response1, ...args) => request2(response1, args),
+ (response2, ...args) => request3(response2, args)
+ ],
+ {
+ immediate: false
+ }
+);
+
+// emit request manually and pass arguments
+send(1, 2, 3);
+```
+
+It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function.
+
+```javascript
+useSerialRequest([
+ methodInstance,
+ (response1, ...args) => request2(response1, args),
+ (response2, ...args) => request3(response2, args)
+]);
+```
+
+### Request error
+
+When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error.
+
+## API
+
+### Hook configuration
+
+Inherit all configurations from [**useRequest**](/api/core-hooks#userequest).
+
+### Responsive data
+
+Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest).
+
+### Action function
+
+Inherit all action functions of [**useRequest**](/api/core-hooks#userequest).
+
+### Event
+
+Inherit all events from [**useRequest**](/api/core-hooks#userequest).
diff --git a/docs/tutorial/05-strategy/11-useSerialWatcher.md b/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md
similarity index 90%
rename from docs/tutorial/05-strategy/11-useSerialWatcher.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md
index beeacda34..9eeb06d23 100644
--- a/docs/tutorial/05-strategy/11-useSerialWatcher.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/11-useSerialWatcher.md
@@ -1,145 +1,144 @@
----
-title: useWatcher with serial
-sidebar_position: 70
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info policy type
-
-use hook
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic usage of alova.
-
-Status update triggers a set of serial requests, which is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions.
-
-## Features
-
-- ✨ A more concise and easy-to-use serial method;
-- ✨Unified request status and callback function;
-- ✨Status update triggers serial execution of multiple requests;
-
-## Example
-
-[serial request](/tutorial/example/serial-request)
-
-## Install
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-## use
-
-### Basic usage
-
-Same usage as `useWatcher`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request.
-
-```javascript
-const {
- // Serial loading status, all requests will be changed to false
- loading,
-
- // The response data of the last request
- data,
-
- // Any request error will record the error message here
- error,
-
- // Manually send a serial request
- send,
-
- // serial request success callback binding function
- onSuccess,
-
- // Serial request error callback binding function, any request error will trigger it
- onError,
-
- // Serial request completion callback binding function
- onComplete
-} = useSerialWatcher(
- [
- // args is the parameter passed in by the send function
- (...args) => request1(args),
-
- // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second
- (response1, ...args) => request2(response1, args),
- (response2, ...args) => request3(response2, args)
- ],
- [watchedState1, watchedState2],
- {
- immediate: true
- }
-);
-
-// Manually trigger the request and pass parameters
-send(1, 2, 3);
-```
-
-It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function.
-
-```javascript
-useSerialRequest([
- methodInstance,
- (response1, ...args) => request2(response1, args),
- (response2, ...args) => request3(response2, args)
-]);
-```
-
-### Request error
-
-When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error.
-
-## API
-
-### Hook configuration
-
-Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher).
-
-### Responsive data
-
-Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher).
-
-### Action function
-
-Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher).
-
-### Event
-
-Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher).
+---
+title: useWatcher with serial
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info policy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+Status update triggers a set of serial requests, which is more concise and easy to use than [serial request in best practice](/tutorial/best-practice/skills), with unified loading status, error, and callback functions.
+
+## Features
+
+- A more concise and easy-to-use serial method;
+- Unified request status and callback function;
+- Status update triggers serial execution of multiple requests;
+
+## Example
+
+[serial request](/tutorial/example/serial-request)
+
+## Install
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## use
+
+### Basic usage
+
+Same usage as `useWatcher`, except that the first parameter is changed to an array of handlers executed serially, and each handler will receive the response data of the previous request.
+
+```javascript
+const {
+ // Serial loading status, all requests will be changed to false
+ loading,
+
+ // The response data of the last request
+ data,
+
+ // Any request error will record the error message here
+ error,
+
+ // Manually send a serial request
+ send,
+
+ // serial request success callback binding function
+ onSuccess,
+
+ // Serial request error callback binding function, any request error will trigger it
+ onError,
+
+ // Serial request completion callback binding function
+ onComplete
+} = useSerialWatcher(
+ [
+ // args is the parameter passed in by the send function
+ (...args) => request1(args),
+
+ // Starting from the second handler, the first parameter is the response data of the previous request, and args is received from the second
+ (response1, ...args) => request2(response1, args),
+ (response2, ...args) => request3(response2, args)
+ ],
+ [watchedState1, watchedState2],
+ {
+ immediate: true
+ }
+);
+
+// Manually trigger the request and pass parameters
+send(1, 2, 3);
+```
+
+It is worth noting that the first item in the handler array can also be specified as a method instance, and the second item must be a function.
+
+```javascript
+useSerialRequest([
+ methodInstance,
+ (response1, ...args) => request2(response1, args),
+ (response2, ...args) => request3(response2, args)
+]);
+```
+
+### Request error
+
+When any of the serial requests is wrong, `onError` will be triggered, and its `event.method` will point to the method instance of the request error.
+
+## API
+
+### Hook configuration
+
+Inherit all configurations of [**useWatcher**](/api/core-hooks#usewatcher).
+
+### Responsive data
+
+Inherit all responsive data from [**useWatcher**](/api/core-hooks#usewatcher).
+
+### Action function
+
+Inherit all action functions of [**useWatcher**](/api/core-hooks#usewatcher).
+
+### Event
+
+Inherit all events from [**useWatcher**](/api/core-hooks#usewatcher).
diff --git a/docs/tutorial/05-strategy/12-useRetriableRequest.md b/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md
similarity index 96%
rename from docs/tutorial/05-strategy/12-useRetriableRequest.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md
index d6fba2230..f902aba4a 100644
--- a/docs/tutorial/05-strategy/12-useRetriableRequest.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/12-useRetriableRequest.md
@@ -1,269 +1,268 @@
----
-title: retriable request
-sidebar_position: 80
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info strategy type
-
-use hook
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic usage of alova.
-
-A use hook that can automatically retry a request failure, you can use it for important requests.
-
-## Example
-
-[Request Retry Demo](/tutorial/example/retriable-hook)
-
-## Features
-
-- ✨Customize the number of retries or judge whether retry is required according to the conditions;
-- ✨ Retry delay mechanism;
-- ✨ Manually stop retrying;
-
-## Install
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-## use
-
-### Basic usage
-
-```javascript
-const {
- // Loading status, always true during retry, until retry succeeds or fails
- loading,
-
- // response data
- data,
-
- // Request error information, every time a request or retry fails, there will be an error instance
- // The last error instance will be overwritten
- error,
-
- // Every time a request or retry fails, the onError event will be triggered
- onError,
-
- // Request retry event, triggered immediately after each retry request is issued
- onRetry,
-
- // request retry failure event
- // The request is not successful after reaching the maximum number of retries, or manually stopping retries will trigger
- onFail,
-
- // request or retry success event
- onSuccess,
-
- // Every request or retry, regardless of success or failure, will trigger the completion event
- onComplete
-} = useRetriableRequest(request);
-```
-
-The maximum number of request retries for `useRetriableRequest` defaults to 3, and each retry will be delayed by 1 second. It will also make a request by default, you can change the behavior by setting `immediate` to false.
-
-### Set the static maximum number of retries
-
-The maximum number of retries indicates the maximum number of times to retry the request after the first request fails. During this period, if the request succeeds, it will stop continuing to retry. The default maximum number of retries is 3, and you can customize the settings in the following ways.
-
-When the request reaches the maximum number of retries and still fails, the `onFail` event will be triggered and the request retry will stop. If you want to continue to retry after the failure, you can call the `send` function, and it will perform a new round request and retry.
-
-```javascript
-const { send } = useRetriableRequest(request, {
- //...
- // highlight-start
- // Set the maximum number of retries to 5
- retry: 5
- // highlight-end
-});
-```
-
-### Dynamically set the maximum number of retries
-
-Maybe sometimes you want to use a certain condition to determine whether to continue to retry. At this time, you can set `retry` as a function that returns a boolean value to dynamically determine whether to continue to retry.
-
-```javascript
-useRetriableRequest(request, {
- //...
- // highlight-start
- // The first parameter is the last error instance, and the parameters passed in from the second parameter to send
- retry(error, ...args) {
- // If the request times out, continue to retry
- return /network timeout/i.test(error.message);
- }
- // highlight-end
-});
-```
-
-### Set delay time
-
-The default retry delay time is 1 second, you can customize the setting in the following ways.
-
-```javascript
-useRetriableRequest(request, {
- //...
- backoff: {
- // highlight-start
- // Set the delay time to 2 seconds
- delay: 2000
- // highlight-end
- }
-});
-```
-
-### Set an unfixed retry delay time
-
-Sometimes you want that the delay time of each request is not fixed, you can set the delay growth multiple in the following way, and the delay time will increase exponentially according to the number of retries.
-
-```javascript
-useRetriableRequest(request, {
- //...
- backoff: {
- delay: 2000,
- // highlight-start
- // When the multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, the third is 8 seconds, and so on.
- multiplier: 2
- // highlight-end
- }
-});
-```
-
-not enough? You can even add a random jitter value to each delay to make it look less regular.
-
-```javascript
-useRetriableRequest(request, {
- //...
- backoff: {
- delay: 2000,
- multiplier: 2,
- // highlight-start
- /**
- * The initial jitter percentage value of the delay request, the range is 0-1
- * When only startQuiver is set, endQuiver defaults to 1
- * For example set to 0.5, it will add 50% to 100% random time on the current delay time
- * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver
- */
- startQuiver: 0.5,
-
- /**
- * The jitter end percentage value of the delayed request, the range is 0-1
- * When only endQuiver is set, startQuiver defaults to 0
- * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time
- * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver
- */
- endQuiver: 0.8;
- // highlight-end
- }
-});
-```
-
-### Manually stop retrying
-
-In some cases, you need to manually stop the retry, whether you are currently requesting or waiting for the next retry, you can use `stop` to stop it.
-
-```javascript
-const { stop } = useRetriableRequest(request, {
- //...
-});
-
-const handleStop = () => {
- stop();
-};
-```
-
-## API
-
-### Hook configuration
-
-Inherit all configurations from [**useRequest**](/api/core-hooks#userequest).
-
-| Name | Description | Type | Default | Version |
-| ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------- | ------- | --- |
-| retry | The maximum number of retries can also be set as a function returning a boolean value to dynamically determine whether to continue to retry. | number | (error: Error, ...args: any[]) => boolean | 3 | - |
-| backoff | Backoff policy, set retry delay time, etc. | [BackoffPolicy](#backoffpolicy) | - | - |
-
-### BackoffPolicy
-
-| Name | Description | Type | Default | Version |
-| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- |
-| delay | Delay time for another request, in milliseconds | number | 1000 | - |
-| multiplier | Specify the delay multiplier, for example, when multiplier is set to 2 and delay is 1 second, the first retry is 1 second, the second is 2 seconds, the third is 4 seconds, and so on | number | 0 | - |
-| startQuiver | The initial jitter percentage value of the delay request, ranging from 0-1. When only startQuiver is set, endQuiver defaults to 1. For example, if it is set to 0.5, it will increase the current delay time by 50% to 100% randomly Time, if endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver | number | 0 | - |
-| endQuiver | The jitter end percentage value of the delayed request, the range is 0-1, when onlyWhen endQuiver is set, startQuiver defaults to 0. For example, if it is set to 0.5, it will add a random time from 0% to 50% to the current delay time. If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver | number | 0 | - |
-
-### Responsive data
-
-Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest).
-
-### Action function
-
-Inherit all action functions of [**useRequest**](/api/core-hooks#userequest).
-
-| name | description | function parameters | return value | version |
-| ---- | ------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | ------- |
-| stop | Stop retrying, it is only valid during retrying, and the onFail event will be triggered immediately after stopping | - | - | - |
-
-### Event
-
-Inherit all events from [**useRequest**](/api/core-hooks#userequest).
-
-| Name | Description | Callback Parameters | Version |
-| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- |
-| onRetry | Retry event bindings, they will fire after a retry is initiated | Retry event instance [RetriableRetryEvent](#retriableretryevent) | - |
-| onFail | Triggered when the request fails. It will be triggered when no more retries are made. For example, when the maximum number of retries is reached, when the retry callback returns false, manually call stop to stop retrying Note: 1 The .onError event will be triggered every time an error is reported. 2. If there are no retries, onError, onComplete and onFail will be triggered at the same time | Retry event instance [RetriableFailEvent](#retriablefailevent) | - |
-
-#### RetriableRetryEvent
-
-Event event instance inherited from alova.
-
-| Name | Description | Type | Default | Version |
-| ---------- | ----------------------------------- | ------ | -------- | ------- |
-| retryTimes | current retry times | number | required | - |
-| retryDelay | The delay time of this retry, in ms | number | required | - |
-
-#### RetriableFailEvent
-
-Event event instance inherited from alova.
-
-| Name | Description | Type | Default | Version |
-| ---------- | ------------------- | ------ | -------- | ------- |
-| retryTimes | current retry times | number | required | - |
+---
+title: retriable request
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+A use hook that can automatically retry a request failure, you can use it for important requests.
+
+## Example
+
+[Request Retry Demo](/tutorial/example/retriable-hook)
+
+## Features
+
+- Customize the number of retries or judge whether retry is required according to the conditions;
+- Retry delay mechanism;
+- Manually stop retrying;
+
+## Install
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## use
+
+### Basic usage
+
+```javascript
+const {
+ // Loading status, always true during retry, until retry succeeds or fails
+ loading,
+
+ // response data
+ data,
+
+ // Request error information, every time a request or retry fails, there will be an error instance
+ // The last error instance will be overwritten
+ error,
+
+ // Every time a request or retry fails, the onError event will be triggered
+ onError,
+
+ // Request retry event, triggered immediately after each retry request is issued
+ onRetry,
+
+ // request retry failure event
+ // The request is not successful after reaching the maximum number of retries, or manually stopping retries will trigger
+ onFail,
+
+ // request or retry success event
+ onSuccess,
+
+ // Every request or retry, regardless of success or failure, will trigger the completion event
+ onComplete
+} = useRetriableRequest(request);
+```
+
+The maximum number of request retries for `useRetriableRequest` defaults to 3, and each retry will be delayed by 1 second. It will also make a request by default, you can change the behavior by setting `immediate` to false.
+
+### Set the static maximum number of retries
+
+The maximum number of retries indicates the maximum number of times to retry the request after the first request fails. During this period, if the request succeeds, it will stop continuing to retry. The default maximum number of retries is 3, and you can customize the settings in the following ways.
+
+When the request reaches the maximum number of retries and still fails, the `onFail` event will be triggered and the request retry will stop. If you want to continue to retry after the failure, you can call the `send` function, and it will perform a new round request and retry.
+
+```javascript
+const { send } = useRetriableRequest(request, {
+ //...
+ // highlight-start
+ // Set the maximum number of retries to 5
+ retry: 5
+ // highlight-end
+});
+```
+
+### Dynamically set the maximum number of retries
+
+Maybe sometimes you want to use a certain condition to determine whether to continue to retry. At this time, you can set `retry` as a function that returns a boolean value to dynamically determine whether to continue to retry.
+
+```javascript
+useRetriableRequest(request, {
+ //...
+ // highlight-start
+ // The first parameter is the last error instance, and the parameters passed in from the second parameter to send
+ retry(error, ...args) {
+ // If the request times out, continue to retry
+ return /network timeout/i.test(error.message);
+ }
+ // highlight-end
+});
+```
+
+### Set delay time
+
+The default retry delay time is 1 second, you can customize the setting in the following ways.
+
+```javascript
+useRetriableRequest(request, {
+ //...
+ backoff: {
+ // highlight-start
+ // Set the delay time to 2 seconds
+ delay: 2000
+ // highlight-end
+ }
+});
+```
+
+### Set an unfixed retry delay time
+
+Sometimes you want that the delay time of each request is not fixed, you can set the delay growth multiple in the following way, and the delay time will increase exponentially according to the number of retries.
+
+```javascript
+useRetriableRequest(request, {
+ //...
+ backoff: {
+ delay: 2000,
+ // highlight-start
+ // When the multiplier is set to 2, the first retry delay is 2 seconds, the second is 4 seconds, the third is 8 seconds, and so on.
+ multiplier: 2
+ // highlight-end
+ }
+});
+```
+
+not enough? You can even add a random jitter value to each delay to make it look less regular.
+
+```javascript
+useRetriableRequest(request, {
+ //...
+ backoff: {
+ delay: 2000,
+ multiplier: 2,
+ // highlight-start
+ /**
+ * The initial jitter percentage value of the delay request, the range is 0-1
+ * When only startQuiver is set, endQuiver defaults to 1
+ * For example set to 0.5, it will add 50% to 100% random time on the current delay time
+ * If endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver
+ */
+ startQuiver: 0.5,
+
+ /**
+ * The jitter end percentage value of the delayed request, the range is 0-1
+ * When only endQuiver is set, startQuiver defaults to 0
+ * For example set to 0.8, it will add a random time from 0% to 80% on the current delay time
+ * If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver
+ */
+ endQuiver: 0.8;
+ // highlight-end
+ }
+});
+```
+
+### Manually stop retrying
+
+In some cases, you need to manually stop the retry, whether you are currently requesting or waiting for the next retry, you can use `stop` to stop it.
+
+```javascript
+const { stop } = useRetriableRequest(request, {
+ //...
+});
+
+const handleStop = () => {
+ stop();
+};
+```
+
+## API
+
+### Hook configuration
+
+Inherit all configurations from [**useRequest**](/api/core-hooks#userequest).
+
+| Name | Description | Type | Default | Version |
+| ------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------------- | ------- | --- |
+| retry | The maximum number of retries can also be set as a function returning a boolean value to dynamically determine whether to continue to retry. | number | (error: Error, ...args: any[]) => boolean | 3 | - |
+| backoff | Backoff policy, set retry delay time, etc. | [BackoffPolicy](#backoffpolicy) | - | - |
+
+### BackoffPolicy
+
+| Name | Description | Type | Default | Version |
+| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------- | ------- |
+| delay | Delay time for another request, in milliseconds | number | 1000 | - |
+| multiplier | Specify the delay multiplier, for example, when multiplier is set to 2 and delay is 1 second, the first retry is 1 second, the second is 2 seconds, the third is 4 seconds, and so on | number | 0 | - |
+| startQuiver | The initial jitter percentage value of the delay request, ranging from 0-1. When only startQuiver is set, endQuiver defaults to 1. For example, if it is set to 0.5, it will increase the current delay time by 50% to 100% randomly Time, if endQuiver has a value, the delay time will be increased by a random value in the range of startQuiver and endQuiver | number | 0 | - |
+| endQuiver | The jitter end percentage value of the delayed request, the range is 0-1, when onlyWhen endQuiver is set, startQuiver defaults to 0. For example, if it is set to 0.5, it will add a random time from 0% to 50% to the current delay time. If startQuiver has a value, the delay time will increase the random value in the range of startQuiver and endQuiver | number | 0 | - |
+
+### Responsive data
+
+Inherit all responsive data from [**useRequest**](/api/core-hooks#userequest).
+
+### Action function
+
+Inherit all action functions of [**useRequest**](/api/core-hooks#userequest).
+
+| name | description | function parameters | return value | version |
+| ---- | ------------------------------------------------------------------------------------------------------------------ | ------------------- | ------------ | ------- |
+| stop | Stop retrying, it is only valid during retrying, and the onFail event will be triggered immediately after stopping | - | - | - |
+
+### Event
+
+Inherit all events from [**useRequest**](/api/core-hooks#userequest).
+
+| Name | Description | Callback Parameters | Version |
+| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ------- |
+| onRetry | Retry event bindings, they will fire after a retry is initiated | Retry event instance [RetriableRetryEvent](#retriableretryevent) | - |
+| onFail | Triggered when the request fails. It will be triggered when no more retries are made. For example, when the maximum number of retries is reached, when the retry callback returns false, manually call stop to stop retrying Note: 1 The .onError event will be triggered every time an error is reported. 2. If there are no retries, onError, onComplete and onFail will be triggered at the same time | Retry event instance [RetriableFailEvent](#retriablefailevent) | - |
+
+#### RetriableRetryEvent
+
+Event event instance inherited from alova.
+
+| Name | Description | Type | Default | Version |
+| ---------- | ----------------------------------- | ------ | -------- | ------- |
+| retryTimes | current retry times | number | required | - |
+| retryDelay | The delay time of this retry, in ms | number | required | - |
+
+#### RetriableFailEvent
+
+Event event instance inherited from alova.
+
+| Name | Description | Type | Default | Version |
+| ---------- | ------------------- | ------ | -------- | ------- |
+| retryTimes | current retry times | number | required | - |
diff --git a/docs/tutorial/05-strategy/13-useSSE.md b/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md
similarity index 93%
rename from docs/tutorial/05-strategy/13-useSSE.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md
index 77d2f05dc..6e24ce943 100644
--- a/docs/tutorial/05-strategy/13-useSSE.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/13-useSSE.md
@@ -1,289 +1,286 @@
----
-title: request by server-send events
-sidebar_position: 120
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info strategy type
-
-use hook
-
-:::
-
-> Before using extension hooks, make sure you are familiar with the basic usage of alova.
-
-A use hook that can automatically retry a request failure, you can use it for important requests.
-
-## Features
-
-- ✨ Simpler and easier-to-use usage.
-- ✨ Automatic connection management.
-
-## Install
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-vue --save
-#yarn
-yarn add @alova/scene-vue
-
-```
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-react --save
-#yarn
-yarn add @alova/scene-react
-
-```
-
-
-
-
-
-```bash
-# npm
-npm install @alova/scene-svelte --save
-#yarn
-yarn add @alova/scene-svelte
-
-```
-
-
-
-
-
-## Usage
-
-
-
-
-```typescript
-import { useSSE } from '@alova/scene-vue';
-
-const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
-const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
- initialData: 'initial-data' // Data initially in `data`
-});
-
-// Connect
-send('value');
-
-console.log(data.value); // Data is updated after receiving an event, by default it is `initialData`
-
-// Corresponds to the `message` event of `eventsource`
-const unbindMessage = onMessage(({ data }) => {
- console.log(data);
-});
-
-const unbindError = onError(({ error }) => {
- console.error('sse error', error);
- close();
-});
-
-// Unbind when needed
-unbindMessage();
-unbindError();
-```
-
-
-
-
-```typescript
-import { useSSE } from '@alova/scene-react';
-
-const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
-const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
- initialData: 'initial-data' // Data initially in `data`
-});
-
-// Connect
-send('value');
-
-console.log(data); // Data is updated after receiving an event, by default it is `initialData`
-
-// Corresponds to the `message` event of `eventsource`
-const unbindMessage = onMessage(({ data }) => {
- console.log(data);
-});
-
-const unbindError = onError(({ error }) => {
- console.error('sse error', error);
- close();
-});
-
-// Unbind when needed
-unbindMessage();
-unbindError();
-```
-
-
-
-
-
-```typescript
-import { useSSE } from '@alova/scene-svelte';
-
-const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
-const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
- initialData: 'initial-data' // Data initially in `data`
-});
-
-// Connect
-send('value');
-
-console.log(data); // Data is updated after receiving an event, by default it is `initialData`
-
-// Corresponds to the `message` event of `eventsource`
-const unbindMessage = onMessage(({ data }) => {
- console.log(data);
-});
-
-const unbindError = onError(({ error }) => {
- console.error('sse error', error);
- close();
-});
-
-// Unbind when needed
-unbindMessage();
-unbindError();
-```
-
-
-
-
-:::warning
-
-Currently, `useSSE` can only connect to one source. This means that when attempting to connect to multiple targets, the previous connection will always be terminated.
-
-:::
-
-``` typescript
-const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method);
-
-send('value1');
-// highlight-start
-send('value2'); // This will terminate the previous connection
-send('value3'); // This will also terminate the previous connection
-// highlight-end
-```
-
-By default, no request is sent. However, by setting immediate = true, you can skip the manual send step.
-
-```typescript
-const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
- // highlight-start
- immediate: true
- // highlight-end
-});
-
-// codes here...
-```
-
-### Binding Custom Events
-
-```typescript
-const { data, readyState, onMessage, on } = useSSE(method);
-
-on('event-name', ({ data }) => {
- console.log(data);
-});
-```
-
-### Global Response Interception
-
-By default, the response data is captured by [the global response interceptors](/tutorial/combine-framework/response). If this is not the desired behavior, you can manually disable it.
-
-```typescript
-const { data, readyState, onMessage, on } = useSSE(method, {
- // highlight-start
- interceptByGlobalResponded: false // Now the data will not be intercepted by the response interceptor
- // highlight-end
-});
-```
-
-## Type Declaration
-
-``` typescript
-const enum SSEHookReadyState {
- CONNECTING = 0,
- OPEN = 1,
- CLOSED = 2
-};
-
-type SSEHookConfig = {
- /**
- * Passed to new EventSource
- */
- withCredentials?: boolean;
-
- /**
- * Whether to be intercepted by the global responded interceptor of Alova instance
- * @default true
- */
- interceptByGlobalResponded?: boolean;
-
- /**
- * Initial data
- */
- initialData?: any;
-
- /**
- * Whether to initiate the request immediately
- * @default false
- */
- immediate?: boolean;
-};
-
-type SSEReturnType = {
- readyState: ExportedType;
- data: ExportedType;
- eventSource: ExportedType;
- /**
- * Manually initiate the request. When `immediate: true` is used, this method is triggered automatically.
- * @param sendArgs Request parameters passed to the method
- */
- send: (...sendArgs: any[]) => Promise;
- /**
- * Close the connection
- */
- close: () => void;
- /**
- * Register a callback function for the EventSource 'open' event
- * @param callback Callback function
- * @returns Function to unregister the callback
- */
- onOpen(callback: SSEOnOpenTrigger): () => void;
-
- /**
- * Register a callback function for the EventSource 'message' event
- * @param callback Callback function
- * @returns Function to unregister the callback
- */
- onMessage(callback: SSEOnMessageTrigger): () => void;
-
- /**
- * Register a callback function for the EventSource 'error' event
- * @param callback Callback function
- * @returns Function to unregister the callback
- */
- onError(callback: SSEOnErrorTrigger): () => void;
-
- /**
- * @param eventName Event name, defaults to 'open' | 'error' | 'message'
- * @param handler Event handler
- */
- on(
- eventName: string,
- handler: (event: AlovaSSEMessageEvent) => void
- ) => () => void;
-};
-```
+---
+title: request by server-send events
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info strategy type
+
+use hook
+
+:::
+
+> Before using extension hooks, make sure you are familiar with the basic usage of alova.
+
+A use hook that can automatically retry a request failure, you can use it for important requests.
+
+## Features
+
+- Simpler and easier-to-use usage.
+- Automatic connection management.
+
+## Install
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-vue --save
+#yarn
+yarn add @alova/scene-vue
+
+```
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-react --save
+#yarn
+yarn add @alova/scene-react
+
+```
+
+
+
+
+
+```bash
+# npm
+npm install @alova/scene-svelte --save
+#yarn
+yarn add @alova/scene-svelte
+
+```
+
+
+
+
+## Usage
+
+
+
+
+```typescript
+import { useSSE } from '@alova/scene-vue';
+
+const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
+ initialData: 'initial-data' // Data initially in `data`
+});
+
+// Connect
+send('value');
+
+console.log(data.value); // Data is updated after receiving an event, by default it is `initialData`
+
+// Corresponds to the `message` event of `eventsource`
+const unbindMessage = onMessage(({ data }) => {
+ console.log(data);
+});
+
+const unbindError = onError(({ error }) => {
+ console.error('sse error', error);
+ close();
+});
+
+// Unbind when needed
+unbindMessage();
+unbindError();
+```
+
+
+
+
+```typescript
+import { useSSE } from '@alova/scene-react';
+
+const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
+ initialData: 'initial-data' // Data initially in `data`
+});
+
+// Connect
+send('value');
+
+console.log(data); // Data is updated after receiving an event, by default it is `initialData`
+
+// Corresponds to the `message` event of `eventsource`
+const unbindMessage = onMessage(({ data }) => {
+ console.log(data);
+});
+
+const unbindError = onError(({ error }) => {
+ console.error('sse error', error);
+ close();
+});
+
+// Unbind when needed
+unbindMessage();
+unbindError();
+```
+
+
+
+
+```typescript
+import { useSSE } from '@alova/scene-svelte';
+
+const method = (value: string) => alova.Get('/api/source', { param: { key: value } });
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
+ initialData: 'initial-data' // Data initially in `data`
+});
+
+// Connect
+send('value');
+
+console.log(data); // Data is updated after receiving an event, by default it is `initialData`
+
+// Corresponds to the `message` event of `eventsource`
+const unbindMessage = onMessage(({ data }) => {
+ console.log(data);
+});
+
+const unbindError = onError(({ error }) => {
+ console.error('sse error', error);
+ close();
+});
+
+// Unbind when needed
+unbindMessage();
+unbindError();
+```
+
+
+
+
+:::warning
+
+Currently, `useSSE` can only connect to one source. This means that when attempting to connect to multiple targets, the previous connection will always be terminated.
+
+:::
+
+```typescript
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method);
+
+send('value1');
+// highlight-start
+send('value2'); // This will terminate the previous connection
+send('value3'); // This will also terminate the previous connection
+// highlight-end
+```
+
+By default, no request is sent. However, by setting immediate = true, you can skip the manual send step.
+
+```typescript
+const { data, eventSource, readyState, onMessage, onError, on, send, close } = useSSE(method, {
+ // highlight-start
+ immediate: true
+ // highlight-end
+});
+
+// codes here...
+```
+
+### Binding Custom Events
+
+```typescript
+const { data, readyState, onMessage, on } = useSSE(method);
+
+on('event-name', ({ data }) => {
+ console.log(data);
+});
+```
+
+### Global Response Interception
+
+By default, the response data is captured by [the global response interceptors](/tutorial/combine-framework/response). If this is not the desired behavior, you can manually disable it.
+
+```typescript
+const { data, readyState, onMessage, on } = useSSE(method, {
+ // highlight-start
+ interceptByGlobalResponded: false // Now the data will not be intercepted by the response interceptor
+ // highlight-end
+});
+```
+
+## Type Declaration
+
+```typescript
+const enum SSEHookReadyState {
+ CONNECTING = 0,
+ OPEN = 1,
+ CLOSED = 2
+};
+
+type SSEHookConfig = {
+ /**
+ * Passed to new EventSource
+ */
+ withCredentials?: boolean;
+
+ /**
+ * Whether to be intercepted by the global responded interceptor of Alova instance
+ * @default true
+ */
+ interceptByGlobalResponded?: boolean;
+
+ /**
+ * Initial data
+ */
+ initialData?: any;
+
+ /**
+ * Whether to initiate the request immediately
+ * @default false
+ */
+ immediate?: boolean;
+};
+
+type SSEReturnType = {
+ readyState: ExportedType;
+ data: ExportedType;
+ eventSource: ExportedType;
+ /**
+ * Manually initiate the request. When `immediate: true` is used, this method is triggered automatically.
+ * @param sendArgs Request parameters passed to the method
+ */
+ send: (...sendArgs: any[]) => Promise;
+ /**
+ * Close the connection
+ */
+ close: () => void;
+ /**
+ * Register a callback function for the EventSource 'open' event
+ * @param callback Callback function
+ * @returns Function to unregister the callback
+ */
+ onOpen(callback: SSEOnOpenTrigger): () => void;
+
+ /**
+ * Register a callback function for the EventSource 'message' event
+ * @param callback Callback function
+ * @returns Function to unregister the callback
+ */
+ onMessage(callback: SSEOnMessageTrigger): () => void;
+
+ /**
+ * Register a callback function for the EventSource 'error' event
+ * @param callback Callback function
+ * @returns Function to unregister the callback
+ */
+ onError(callback: SSEOnErrorTrigger): () => void;
+
+ /**
+ * @param eventName Event name, defaults to 'open' | 'error' | 'message'
+ * @param handler Event handler
+ */
+ on(
+ eventName: string,
+ handler: (event: AlovaSSEMessageEvent) => void
+ ) => () => void;
+};
+```
diff --git a/docs/tutorial/05-strategy/14-rateLimitMiddleware.md b/versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md
similarity index 85%
rename from docs/tutorial/05-strategy/14-rateLimitMiddleware.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md
index aacc9c0c4..e816220f7 100644
--- a/docs/tutorial/05-strategy/14-rateLimitMiddleware.md
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/14-rateLimitMiddleware.md
@@ -1,8 +1,7 @@
----
-title: Request rate limit
-sidebar_position: 130
----
-
-Set the number of requests that should be executed immediately for each interval, and other requests will be automatically delayed.
-
-coming soon...
+---
+title: Request rate limit
+---
+
+Set the number of requests that should be executed immediately for each interval, and other requests will be automatically delayed.
+
+coming soon...
diff --git a/docs/tutorial/05-strategy/README.md b/versioned_docs/version-2.x/tutorial/05-strategy/README.md
similarity index 100%
rename from docs/tutorial/05-strategy/README.md
rename to versioned_docs/version-2.x/tutorial/05-strategy/README.md
diff --git a/docs/tutorial/05-strategy/_category_.json b/versioned_docs/version-2.x/tutorial/05-strategy/_category_.json
similarity index 89%
rename from docs/tutorial/05-strategy/_category_.json
rename to versioned_docs/version-2.x/tutorial/05-strategy/_category_.json
index 03345525b..34e0bcbdb 100644
--- a/docs/tutorial/05-strategy/_category_.json
+++ b/versioned_docs/version-2.x/tutorial/05-strategy/_category_.json
@@ -1,3 +1,3 @@
-{
- "label": "Strategy"
-}
+{
+ "label": "Strategy"
+}
diff --git a/docs/tutorial/06-advanced/01-use-fetcher.md b/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md
similarity index 96%
rename from docs/tutorial/06-advanced/01-use-fetcher.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md
index f51eab080..ededcbd1f 100644
--- a/docs/tutorial/06-advanced/01-use-fetcher.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/01-use-fetcher.md
@@ -1,307 +1,306 @@
----
-title: Fetch Data
-sidebar_position: 10
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-When you have the following needs:
-
-1. Preload the data that will be used in subsequent processes and store it in the cache, so that users no longer have to wait for the data loading process;
-2. Conveniently implement cross-page data update (similar to global state), for example, modify an item in the todo list and then re-fetch the latest data, and the interface will be refreshed after the response.
-
-`useFetcher` is the hook used to implement the above scenario. The response data obtained through it cannot be received directly, but the data fetched through it will not only update the cache, but also update the corresponding state, thereby re-rendering the view.
-
-## Preload data
-
-Let's implement a paging list to automatically preload the next page of data. Before preloading data, please make sure that the Method instance used has enabled caching.
-
-
-
-
-```html
-
-
Fetching...
-
-
-
-
-```
-
-
-
-
-```jsx
-import { useState } from 'react';
-
-//method instance creation function
-const getTodoList = currentPage => {
- return alovaInstance.Get('/todo/list', {
- localCache: 60000,
- params: {
- currentPage,
- pageSize: 10
- }
- });
-};
-
-const App = () => {
- const {
- // The fetching attribute is the same as loading. It is true when sending the fetch request and false after the request is completed.
- fetching,
- error,
- onSuccess,
- onError,
- onComplete,
-
- // Only after calling fetch will a request be sent to fetch data. You can call fetch repeatedly to fetch data from different interfaces.
- fetch
- } = useFetcher({
- updateState: false
- });
- const [currentPage, setCurrentPage] = useState(1);
- const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], {
- immediate: true
- });
-
- // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page.
- onSuccess(() => {
- fetch(getTodoList(currentPage + 1));
- });
-
- return (
- <>
- {fetching ?
-
-
-
-```
-
-
-
-
-:::warning
-
-The above example is set `updateState` to false when calling `useFetcher`. This is because the data fetching will automatically trigger a states cross-component updating by default, causing the view to be re-rendered. When preload data is the same as the currently requested data. You can set it to false to avoid affecting view errors.
-
-:::
-
-## Update views across modules/components
-
-Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically fetch the data of the current page.
-
-> The Method instance matcher is used to find method instances that meet the conditions among the requested method instances.
-
-First, set a name for the method instance in the todo list, which is used to filter out the required Method instance when the Method instance cannot be specified directly.
-
-```javascript title="api/todoList.js"
-const getTodoList = currentPage => {
- return alovaInstance.Get('/todo/list', {
- // highlight-start
- name: 'todoList',
- // highlight-end
- params: {
- currentPage,
- pageSize: 10
- }
- });
-};
-```
-
-Then in the `EditTodo` component, use the `fetch` function to dynamically find the last name of `todoList` in the requested Method instance to fetch data.
-
-```javascript title="EditTodo Component"
-const { fetch } = useFetcher();
-
-// Trigger data fetch in event
-const handleSubmit = () => {
- // submit data...
- await fetch({
- name: 'todoList',
- filter: (method, index, ary) => {
- // Return true to specify the Method instance that needs to be fetched
- return index === ary.length - 1;
- }
- });
-};
-```
-
-:::warning Notes
-
-useFetcher only updates the cache after the request is completed, and if this Method is foundIf the instance has been requested using useHook before, the `data` state created by this useHook will also be updated to ensure that the page data is consistent. This is the guarantee that `useFetcher` is used to update views across modules/components.
-
-:::
-
-> For more methods of using `Method` instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher).
-
-## Force sending request
-
-Same as `useRequest` and `useWatcher`, please read [Force Request](/tutorial/cache/force-request) for more information.
-
-## Bind response callback
-
-useFetcher also supports binding `onSuccess/onError/onComplete` callback functions.
-
-```javascript
-const { onSuccess, onError, onComplete } = useFetcher();
-```
-
-Please read [Response Processing](/tutorial/combine-framework/response) for details.
-
-## send function parameter passing rules
-
-Different from `useRequest` and `useWatcher`, the custom parameters of the fetch function start from the second parameter, and they will also be received by the event callback and `force` function respectively.
-
-```javascript
-const { onSuccess, fetch } = useFetcher();
-onSuccess(({ sendArgs }) => {
- //The value of sendArgs is ['test arg']
-});
-
-fetch(getTodoList(), 'test arg');
-```
-
-For details, please read [send function parameter passing rules](/tutorial/combine-framework/receive-params).
-
-## Comparison between useRequest and useFetcher
-
-1. useFetcher does not return the `data` field, the pre-fetched data will be saved in the cache, and the status data of the corresponding location will be updated;
-2. Renamed `loading` to `fetching`;
-3. There is no `send` function, but there is a `fetch` function. You can reuse the fetch function to fetch data from different interfaces. At this time, you can use the `fetching` and `error` states to uniformly render the view to achieve unified processing. the goal of;
+---
+title: Fetch Data
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+When you have the following needs:
+
+1. Preload the data that will be used in subsequent processes and store it in the cache, so that users no longer have to wait for the data loading process;
+2. Conveniently implement cross-page data update (similar to global state), for example, modify an item in the todo list and then re-fetch the latest data, and the interface will be refreshed after the response.
+
+`useFetcher` is the hook used to implement the above scenario. The response data obtained through it cannot be received directly, but the data fetched through it will not only update the cache, but also update the corresponding state, thereby re-rendering the view.
+
+## Preload data
+
+Let's implement a paging list to automatically preload the next page of data. Before preloading data, please make sure that the Method instance used has enabled caching.
+
+
+
+
+```html
+
+
Fetching...
+
+
+
+
+```
+
+
+
+
+```jsx
+import { useState } from 'react';
+
+//method instance creation function
+const getTodoList = currentPage => {
+ return alovaInstance.Get('/todo/list', {
+ localCache: 60000,
+ params: {
+ currentPage,
+ pageSize: 10
+ }
+ });
+};
+
+const App = () => {
+ const {
+ // The fetching attribute is the same as loading. It is true when sending the fetch request and false after the request is completed.
+ fetching,
+ error,
+ onSuccess,
+ onError,
+ onComplete,
+
+ // Only after calling fetch will a request be sent to fetch data. You can call fetch repeatedly to fetch data from different interfaces.
+ fetch
+ } = useFetcher({
+ updateState: false
+ });
+ const [currentPage, setCurrentPage] = useState(1);
+ const { data, onSuccess } = useWatcher(() => getTodoList(currentPage), [currentPage], {
+ immediate: true
+ });
+
+ // After the current page is loaded successfully, pass in the method instance of the next page to pre-fetch the data of the next page.
+ onSuccess(() => {
+ fetch(getTodoList(currentPage + 1));
+ });
+
+ return (
+ <>
+ {fetching ?
+
+
+
+```
+
+
+
+
+:::warning
+
+The above example is set `updateState` to false when calling `useFetcher`. This is because the data fetching will automatically trigger a states cross-component updating by default, causing the view to be re-rendered. When preload data is the same as the currently requested data. You can set it to false to avoid affecting view errors.
+
+:::
+
+## Update views across modules/components
+
+Next, we will modify a todo data and re-fetch the latest todo list data to update the view. We may not know which page the todo list is currently on. In this case, when using the `fetch` function, we can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically fetch the data of the current page.
+
+> The Method instance matcher is used to find method instances that meet the conditions among the requested method instances.
+
+First, set a name for the method instance in the todo list, which is used to filter out the required Method instance when the Method instance cannot be specified directly.
+
+```javascript title="api/todoList.js"
+const getTodoList = currentPage => {
+ return alovaInstance.Get('/todo/list', {
+ // highlight-start
+ name: 'todoList',
+ // highlight-end
+ params: {
+ currentPage,
+ pageSize: 10
+ }
+ });
+};
+```
+
+Then in the `EditTodo` component, use the `fetch` function to dynamically find the last name of `todoList` in the requested Method instance to fetch data.
+
+```javascript title="EditTodo Component"
+const { fetch } = useFetcher();
+
+// Trigger data fetch in event
+const handleSubmit = () => {
+ // submit data...
+ await fetch({
+ name: 'todoList',
+ filter: (method, index, ary) => {
+ // Return true to specify the Method instance that needs to be fetched
+ return index === ary.length - 1;
+ }
+ });
+};
+```
+
+:::warning Notes
+
+useFetcher only updates the cache after the request is completed, and if this Method is foundIf the instance has been requested using useHook before, the `data` state created by this useHook will also be updated to ensure that the page data is consistent. This is the guarantee that `useFetcher` is used to update views across modules/components.
+
+:::
+
+> For more methods of using `Method` instance matcher, see [Method instance matcher](/tutorial/advanced/method-matcher).
+
+## Force sending request
+
+Same as `useRequest` and `useWatcher`, please read [Force Request](/tutorial/cache/force-request) for more information.
+
+## Bind response callback
+
+useFetcher also supports binding `onSuccess/onError/onComplete` callback functions.
+
+```javascript
+const { onSuccess, onError, onComplete } = useFetcher();
+```
+
+Please read [Response Processing](/tutorial/combine-framework/response) for details.
+
+## send function parameter passing rules
+
+Different from `useRequest` and `useWatcher`, the custom parameters of the fetch function start from the second parameter, and they will also be received by the event callback and `force` function respectively.
+
+```javascript
+const { onSuccess, fetch } = useFetcher();
+onSuccess(({ sendArgs }) => {
+ //The value of sendArgs is ['test arg']
+});
+
+fetch(getTodoList(), 'test arg');
+```
+
+For details, please read [send function parameter passing rules](/tutorial/combine-framework/receive-params).
+
+## Comparison between useRequest and useFetcher
+
+1. useFetcher does not return the `data` field, the pre-fetched data will be saved in the cache, and the status data of the corresponding location will be updated;
+2. Renamed `loading` to `fetching`;
+3. There is no `send` function, but there is a `fetch` function. You can reuse the fetch function to fetch data from different interfaces. At this time, you can use the `fetching` and `error` states to uniformly render the view to achieve unified processing. the goal of;
diff --git a/docs/tutorial/06-advanced/02-update-across-components.md b/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md
similarity index 97%
rename from docs/tutorial/06-advanced/02-update-across-components.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md
index a8a814256..bc982e6d8 100644
--- a/docs/tutorial/06-advanced/02-update-across-components.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/02-update-across-components.md
@@ -1,102 +1,101 @@
----
-title: Update states across components
-sidebar_position: 20
----
-
-There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated without resetting the situation. For edited content, `useFetcher` is no longer applicable.
-
-At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules.
-
-[Here is an example of `updateState`](/tutorial/example/update-state)
-
-## Use method instance to find response states
-
-When determining the method instance corresponding to the updated response state, you can pass in this method instance in `updateState`. It will find whether there is a corresponding response state under this instance and provide it to you for modification in the callback function. Finally Just return the modified data.
-
-```javascript
-import { updateState } from 'alova';
-
-//Todo item being edited
-const editingTodo = {
- id: 1,
- title: 'todo1',
- time: '09:00'
-};
-
-const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false });
-onSuccess(() => {
- // highlight-start
- // Fixed modification of todo data on the first page
- // updateState will return whether the update is successful
- const updated = updateState(getTodoList(1), todoList => {
- return todoList.map(item => {
- if (item.id === editingTodo.id) {
- return {
- ...item,
- ...editingTodo
- };
- }
- return item;
- });
- });
- // highlight-end
-});
-```
-
-:::warning note
-
-1. When updating the state through `updateState`, if the cache (memory cache and persistent cache) is detected, the new data update cache will also be updated.
-2. Only when a request has been initiated using useRequest or useWatcher, alova will manage the states returned by the hook. The reason is that the response states is generated and saved through a Method instance, but when no request is initiated, the url and URL in the Method instance are Parameters such as params, query, and headers are still uncertain.
-
-:::
-
-## Dynamically update response states
-
-Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList.
-
-```javascript
-updateState('todoList', todoListRaw => {
- todoListRaw.push({
- title: 'new todo',
- time: '10:00'
- });
- return todoListRaw;
-});
-```
-
-The [Method instance matcher](/tutorial/advanced/method-matcher) will be introduced in detail in subsequent chapters.
-
-## Listen for matching events
-
-When dynamically updating the response state, sometimes you may want to do some processing when a method instance is matched, or you may want to obtain the matching method instance. `updateState` can also pass in a third parameter to set a matching event to achieve these purposes. .
-
-```javascript
-updateState(
- 'todoList',
- todoListRaw => {
- // ...
- },
- {
- // Called when a method instance is matched, and the parameter is the matched method instance.
- onMatch: method => {
- // ...
- }
- }
-);
-```
-
-:::warning ⚠️ Please make sure the component is not destroyed
-
-By default, `updateState` will look for the response state created by alova's useHooks when sending a request. However, to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response states has not been destroyed, otherwise the corresponding response states will not be found and the update will fail.
-
-This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions:
-
-1. Persist the page components to ensure that the updated states can still be found;
-2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect.
-
-:::
-
-## Notes
-
-1. In actual use, whether you use `useRequest` or `useWatcher` to send a request, you can call the `send` function to specify different parameters to send the request repeatedly. The response states returned by these use hooks will be used by multiple method instances. Reference, so you can choose any method instance to match the same response states value;
-2. When dynamically searching and updating the response states, the method instance matcher finds multiple method instances, and the first instance will prevail;
+---
+title: Update states across components
+---
+
+There is a scenario where when the user clicks on an item in the todo list, enters the todo details page and edits it, at this time we hope that the todo list data on the previous page will also be updated without resetting the situation. For edited content, `useFetcher` is no longer applicable.
+
+At this time, you can use `updateState` to update the existing responsive state under any module/page. It can find and modify the responsive state in other modules.
+
+[Here is an example of `updateState`](/tutorial/example/update-state)
+
+## Use method instance to find response states
+
+When determining the method instance corresponding to the updated response state, you can pass in this method instance in `updateState`. It will find whether there is a corresponding response state under this instance and provide it to you for modification in the callback function. Finally Just return the modified data.
+
+```javascript
+import { updateState } from 'alova';
+
+//Todo item being edited
+const editingTodo = {
+ id: 1,
+ title: 'todo1',
+ time: '09:00'
+};
+
+const { send, onSuccess } = useRequest(createTodoPoster, { immediate: false });
+onSuccess(() => {
+ // highlight-start
+ // Fixed modification of todo data on the first page
+ // updateState will return whether the update is successful
+ const updated = updateState(getTodoList(1), todoList => {
+ return todoList.map(item => {
+ if (item.id === editingTodo.id) {
+ return {
+ ...item,
+ ...editingTodo
+ };
+ }
+ return item;
+ });
+ });
+ // highlight-end
+});
+```
+
+:::warning note
+
+1. When updating the state through `updateState`, if the cache (memory cache and persistent cache) is detected, the new data update cache will also be updated.
+2. Only when a request has been initiated using useRequest or useWatcher, alova will manage the states returned by the hook. The reason is that the response states is generated and saved through a Method instance, but when no request is initiated, the url and URL in the Method instance are Parameters such as params, query, and headers are still uncertain.
+
+:::
+
+## Dynamically update response states
+
+Maybe sometimes you are not sure that you need to update the response states under the method, but you know how to find the cached data that needs to be invalidated. We can use [Method instance matcher](/tutorial/advanced/method-matcher) to dynamically Find the corresponding method instance. The following example shows adding a piece of data to the list corresponding to the method instance named todoList.
+
+```javascript
+updateState('todoList', todoListRaw => {
+ todoListRaw.push({
+ title: 'new todo',
+ time: '10:00'
+ });
+ return todoListRaw;
+});
+```
+
+The [Method instance matcher](/tutorial/advanced/method-matcher) will be introduced in detail in subsequent chapters.
+
+## Listen for matching events
+
+When dynamically updating the response state, sometimes you may want to do some processing when a method instance is matched, or you may want to obtain the matching method instance. `updateState` can also pass in a third parameter to set a matching event to achieve these purposes. .
+
+```javascript
+updateState(
+ 'todoList',
+ todoListRaw => {
+ // ...
+ },
+ {
+ // Called when a method instance is matched, and the parameter is the matched method instance.
+ onMatch: method => {
+ // ...
+ }
+ }
+);
+```
+
+:::warning ⚠️ Please make sure the component is not destroyed
+
+By default, `updateState` will look for the response state created by alova's useHooks when sending a request. However, to prevent memory overflow, the destruction of a component will also recycle all the states created internally, so please make sure you use `updateState` It is hoped that the container component corresponding to the updated response states has not been destroyed, otherwise the corresponding response states will not be found and the update will fail.
+
+This problem often occurs when updating states across pages. What we tend to overlook is that by default, the previous page has been destroyed when the page jumps. Therefore, if you want to update states across pages, here are two suggestions:
+
+1. Persist the page components to ensure that the updated states can still be found;
+2. Use [setCache](/tutorial/cache/set-and-query) instead of `updateState`. The principle is that when the request for the previous page exists in the cache, update its cache to ensure that when the page is created again, the The request can hit the updated cache to achieve the same effect.
+
+:::
+
+## Notes
+
+1. In actual use, whether you use `useRequest` or `useWatcher` to send a request, you can call the `send` function to specify different parameters to send the request repeatedly. The response states returned by these use hooks will be used by multiple method instances. Reference, so you can choose any method instance to match the same response states value;
+2. When dynamically searching and updating the response states, the method instance matcher finds multiple method instances, and the first instance will prevail;
diff --git a/docs/tutorial/06-advanced/03-method-matcher.md b/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md
similarity index 96%
rename from docs/tutorial/06-advanced/03-method-matcher.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md
index 1666b3b22..b582e48b4 100644
--- a/docs/tutorial/06-advanced/03-method-matcher.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/03-method-matcher.md
@@ -1,141 +1,140 @@
----
-title: Method Matcher
-sidebar_position: 30
----
-
-A method matcher is a method that dynamically finds a method instance in a list of requested method snapshots. It is generally used. When developers are not sure which method to use, they can use the method matcher to search according to certain rules.
-
-## Matching rules
-
-When a request is made using a method instance, it will be saved as a snapshot. The method matcher looks in these method snapshots based on the `name` attribute set by the method instance. Multiple matchers are allowed to set the same `name`.
-
-Method instance matching types are as follows:
-
-```typescript
-type MethodFilter =
- | string
- | RegExp
- | {
- name: string | RegExp;
- filter: (method: Method, index: number, methods: Method[]) => boolean;
-
- // Optional parameter, if the alova object is passed in, it will only match the Method instance created by this alova, otherwise it will match the Method instances of all alova instances.
- alova?: Alova;
- };
-```
-
-The method instance matcher can be used in the following functions.
-
-- [setCache](/tutorial/cache/set-and-query)
-- [queryCache](/tutorial/cache/set-and-query)
-- [invalidateCache](/tutorial/cache/manually-invalidate)
-- [updateState](/tutorial/advanced/update-across-components)
-- [useFetcher.fetch](/tutorial/advanced/use-fetcher)
-
-## Match by name attribute
-
-Matching is performed by passing in the complete instance name, and its matching result is an array.
-
-```javascript
-// Each time getTodoList is called, a new method instance will be generated, and their names are the same.
-const getTodoList = currentPage =>
- alova.Get('/todo/list', {
- // highlight-start
- name: 'todoList'
- // highlight-end
- // ...
- });
-
-//The following means invalidating the cache of all Method instances with name 'todoList'
-invalidateCache('todoList');
-```
-
-## Match by regular expression
-
-By passing in a regular expression for matching, any method instance whose name matches the regular expression will be matched, and its result is also an array.
-
-```javascript
-// The following means invalidating the cache of all Method instances whose names start with 'todo'
-invalidateCache(/^todo/);
-```
-
-## Filter matching results
-
-Further filter method instances that do not meet the conditions by specifying `filter`. The filter function is used in the same way as Array.prototype.filter. Returning true indicates successful matching, and returning false indicates failure. See the type declaration above for details.
-
-Let's look at a few examples.
-
-**Invalidate the cache of the last method instance with a specific name**
-
-```javascript
-invalidateCache({
- name: 'todoList',
- filter: (method, index, methods) => index === methods.length - 1
-});
-```
-
-**Set the cache of the last method instance of a specific name created by `alovaInst`**
-
-```javascript
-setCache(
- {
- name: /^todo/,
- filter: (method, index, methods) => index === methods.length - 1,
-
- // If the alova parameter is passed, only the Method instances created by this alova instance will be matched, otherwise it will be matched in all Method instances.
- alova: alovaInst
- },
- newCache
-);
-```
-
-**Repulse the last requested data from the todo list**
-
-```javascript
-const { fetch } = useFetcher();
-fetch({
- name: 'todoList',
- filter: (method, index, methods) => index === methods.length - 1
-});
-```
-
-> The alova parameter can further narrow the matching scope.
-
-## Differences in use in different functions
-
-### invalidateCache
-
-Apply the set of all matching Method instances, that is, invalidate the cache corresponding to all matching Method instances.
-
-### setCache
-
-All matching Method instance collections are applied. When static data is passed in, all Method instance caches are set to the same value. When the callback function is passed in, this function will be called cyclically and the return value will be used as cache data.
-
-### updateState
-
-The first matching Method instance is applied.
-
-### fetch
-
-The first matching Method instance is applied, i.e. the data will only be pulled once.
-
-## Limit instance snapshots
-
-`[v2.20.0+]` By default, 1000 method instance snapshots will be saved, otherwise memory overflow may occur in frequent request scenarios. You can also adjust the limit as needed.
-
-```js
-import { globalConfig } from 'alova';
-
-globalConfig({
- // Limit saving to 500 instance snapshots
- limitSnapshots: 500
-});
-```
-
-When set to 0, instance snapshots are no longer saved and the method matcher cannot be used.
-
-```js
-globalConfig({
- limitSnapshots: 0
-});
-```
+---
+title: Method Matcher
+---
+
+A method matcher is a method that dynamically finds a method instance in a list of requested method snapshots. It is generally used. When developers are not sure which method to use, they can use the method matcher to search according to certain rules.
+
+## Matching rules
+
+When a request is made using a method instance, it will be saved as a snapshot. The method matcher looks in these method snapshots based on the `name` attribute set by the method instance. Multiple matchers are allowed to set the same `name`.
+
+Method instance matching types are as follows:
+
+```typescript
+type MethodFilter =
+ | string
+ | RegExp
+ | {
+ name: string | RegExp;
+ filter: (method: Method, index: number, methods: Method[]) => boolean;
+
+ // Optional parameter, if the alova object is passed in, it will only match the Method instance created by this alova, otherwise it will match the Method instances of all alova instances.
+ alova?: Alova;
+ };
+```
+
+The method instance matcher can be used in the following functions.
+
+- [setCache](/tutorial/cache/set-and-query)
+- [queryCache](/tutorial/cache/set-and-query)
+- [invalidateCache](/tutorial/cache/manually-invalidate)
+- [updateState](/tutorial/advanced/update-across-components)
+- [useFetcher.fetch](/tutorial/advanced/use-fetcher)
+
+## Match by name attribute
+
+Matching is performed by passing in the complete instance name, and its matching result is an array.
+
+```javascript
+// Each time getTodoList is called, a new method instance will be generated, and their names are the same.
+const getTodoList = currentPage =>
+ alova.Get('/todo/list', {
+ // highlight-start
+ name: 'todoList'
+ // highlight-end
+ // ...
+ });
+
+//The following means invalidating the cache of all Method instances with name 'todoList'
+invalidateCache('todoList');
+```
+
+## Match by regular expression
+
+By passing in a regular expression for matching, any method instance whose name matches the regular expression will be matched, and its result is also an array.
+
+```javascript
+// The following means invalidating the cache of all Method instances whose names start with 'todo'
+invalidateCache(/^todo/);
+```
+
+## Filter matching results
+
+Further filter method instances that do not meet the conditions by specifying `filter`. The filter function is used in the same way as Array.prototype.filter. Returning true indicates successful matching, and returning false indicates failure. See the type declaration above for details.
+
+Let's look at a few examples.
+
+**Invalidate the cache of the last method instance with a specific name**
+
+```javascript
+invalidateCache({
+ name: 'todoList',
+ filter: (method, index, methods) => index === methods.length - 1
+});
+```
+
+**Set the cache of the last method instance of a specific name created by `alovaInst`**
+
+```javascript
+setCache(
+ {
+ name: /^todo/,
+ filter: (method, index, methods) => index === methods.length - 1,
+
+ // If the alova parameter is passed, only the Method instances created by this alova instance will be matched, otherwise it will be matched in all Method instances.
+ alova: alovaInst
+ },
+ newCache
+);
+```
+
+**Repulse the last requested data from the todo list**
+
+```javascript
+const { fetch } = useFetcher();
+fetch({
+ name: 'todoList',
+ filter: (method, index, methods) => index === methods.length - 1
+});
+```
+
+> The alova parameter can further narrow the matching scope.
+
+## Differences in use in different functions
+
+### invalidateCache
+
+Apply the set of all matching Method instances, that is, invalidate the cache corresponding to all matching Method instances.
+
+### setCache
+
+All matching Method instance collections are applied. When static data is passed in, all Method instance caches are set to the same value. When the callback function is passed in, this function will be called cyclically and the return value will be used as cache data.
+
+### updateState
+
+The first matching Method instance is applied.
+
+### fetch
+
+The first matching Method instance is applied, i.e. the data will only be pulled once.
+
+## Limit instance snapshots
+
+`[v2.20.0+]` By default, 1000 method instance snapshots will be saved, otherwise memory overflow may occur in frequent request scenarios. You can also adjust the limit as needed.
+
+```js
+import { globalConfig } from 'alova';
+
+globalConfig({
+ // Limit saving to 500 instance snapshots
+ limitSnapshots: 500
+});
+```
+
+When set to 0, instance snapshots are no longer saved and the method matcher cannot be used.
+
+```js
+globalConfig({
+ limitSnapshots: 0
+});
+```
diff --git a/docs/tutorial/06-advanced/04-middleware.md b/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md
similarity index 96%
rename from docs/tutorial/06-advanced/04-middleware.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md
index 2ffa7b652..ac41e8e79 100644
--- a/docs/tutorial/06-advanced/04-middleware.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/04-middleware.md
@@ -1,405 +1,404 @@
----
-title: Middleware
-sidebar_position: 40
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-Request middleware is an asynchronous function. it provides a powerful ability to control almost all behaviors of a request. If you just use alova, then you probably don't need to use request middleware, because it is mainly used to complete custom request strategies, no matter simple or complex request strategies, you may use it, let's look at it next What magical powers does it have.
-
-## Middleware function
-
-Request middleware is an async function, You can define request middleware in `useRequest`, `useWatcher`, `useFetcher`. The following is a simple request middleware, which prints some information before and after the request without changing any request behavior.
-
-```javascript
-useRequest(todoList, {
- async middleware(_, next) {
- console.log('before request');
- await next();
- console.log('after request');
- }
-});
-```
-
-Here are a few things you need to know about the `next` function call. This function is also an asynchronous function. Calling it can continue to send requests. At this time, the _loading_ state will be set to true, and then the request will be sent. The return value of next is a Promise instance with the response data, you can manipulate the return value in the middleware function.
-
-## Control response data
-
-The return value of the middleware function will be used as the response data of this request to participate in subsequent processing. If the middleware does not return any data but calls `next`, the response data of this request will be used for subsequent processing.
-
-```javascript
-// The modified result will be used as the response data
-useRequest(todoList, {
- async middleware(_, next) {
- const result = await next();
- result.code = 500;
- return result;
- }
-});
-
-// Will participate in subsequent processing with the response data of this request
-useRequest(todoList, {
- async middleware(_, next) {
- await next();
- }
-});
-
-// will respond with the string abc
-useRequest(todoList, {
- async middleware(_, next) {
- await next();
- return 'abc';
- }
-});
-```
-
-There is also a special case here. When `next` is not called and there is no return value, subsequent processing will not be performed, which means that _onSuccess_, _onError_, _onComplete_ response events will not be triggered.
-
-```javascript
-useRequest(todoList, {
- async middleware() {}
-});
-```
-
-## change request
-
-Sometimes you want to change the request. At this time, you can specify another method instance in `next`, and the information in this method will be requested when sending the request. At the same time, you can also set whether to force the request through `next` Penetrating the cache is also very simple.
-
-```javascript
-useRequest(todoList, {
- async middleware(_, next) {
- await next({
- // Change the requested method instance
- method: newMethodInstance,
-
- // Whether to force the request this time
- force: true
- });
- }
-});
-```
-
-## Control errors
-
-### Catch errors
-
-In the middleware, you can capture the request error generated in `next`, after capturing, the global `onError` hook will no longer be triggered.
-
-```javascript
-useRequest(todoList, {
- async middleware(_, next) {
- try {
- await next();
- } catch (e) {
- console.error('Error caught', e);
- }
- }
-});
-```
-
-### Throw an error
-
-Of course, you can also throw a custom error in the middleware, even if the request is normal, it will enter the request error process.
-
-```javascript
-// No request is sent, and global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned
-useRequest(todoList, {
- async middleware(_, next) {
- throw new Error('error on before request');
- await next();
- }
-});
-
-// After request is success, global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned
-useRequest(todoList, {
- async middleware(_, next) {
- await next();
- throw new Error('error on after request');
- }
-});
-```
-
-## Control response delay
-
-In the middleware, we can delay the response or respond in advance. In the case of advance, although the response data cannot be obtained, some other data can be returned as the response data to participate in subsequent processing.
-
-```javascript
-// Delay response for 1 second
-useRequest(todoList, {
- async middleware(_, next) {
- await new Promise(resolve => {
- setTimeout(resolve, 1000);
- });
- return next();
- }
-});
-
-// Respond immediately and use the string abc as the response data
-useRequest(todoList, {
- async middleware(_, next) {
- return 'abc';
- }
-});
-```
-
-## More than that
-
-**So far, all we have mentioned is the use of the second parameter `next` of the middleware, so what is the first parameter for? **
-
-The first parameter of the middleware contains some information about this request, as well as the control functions for the status and events returned in useHook such as `loading`, `data` and `onSuccess`. Let's move on!
-
-## Included request information
-
-
-
-
-The following is the request information contained in the middleware of useRequest and useWatcher
-
-```javascript
-async function alovaFrontMiddleware(context, next) {
- // The method instance of this request
- context.method;
-
- // The parameter array sent by the send function, the default is []
- context.sendArgs;
-
- // The cache data hit by this request
- context.cachedResponse;
-
- // configuration collection of useHook
- context.config;
-
- // The various states returned by useHook, including the following attributes
- // loading, data, error, downloading, uploading, and additional states managed by managedStates
- context.frontStates;
- //...
-}
-```
-
-
-
-
-The following is the request information contained in the middleware of useFetcher
-
-```javascript
-async function alovaFetcherMiddleware(context, next) {
- // The method instance of this request
- context.method;
-
- // The parameter group passed in by the fetch of useFetcher, the default is []
- context.fetchArgs;
-
- // The cache data hit by this request
- context.cachedResponse;
-
- // configuration collection of useHook
- context.config;
-
- // The various states returned by useHook, including the following attributes
- // fetching, error, downloading, uploading
- context.fetchStates;
- //...
-}
-```
-
-
-
-
-Next, let's take a look at what controls are available.
-
-## Modify responsive data
-
-Use `context.update` to modify reactive data.
-
-
-
-
-```javascript
-async function alovaFrontMiddleware(context, next) {
- context.update({
- // Modify the loading status to true in advance
- loading: true,
-
- // Modify the data value, such as setting custom initialization data
- data: {
- /* ... */
- }
- });
- //...
-}
-```
-
-
-
-
-```javascript
-async function alovaFetcherMiddleware(context, next) {
- context.update({
- // Modify the fetching status to true in advance
- fetching: true,
-
- // Modify the value of error
- error: new Error('custom midleware error')
- });
- //...
-}
-```
-
-
-
-
-## Decorate events
-
-You can also decorate _onSuccess_, _onError_, _onComplete_ callback functions in middleware to make them richer, such as changing the parameters of the callback function, or receiving the return value of the callback function to achieve more functions.
-
-You can use `decorateSuccess`, `decorateError`, `decorateComplete` functions to decorate callback functions. The following takes the success callback as an example, which is decorated in 3 places:
-
-1. Added `custom` attribute to event object;
-2. Added a second parameter to the success callback function, the value is `extra data`;
-3. Receive the value of the second success callback function and print it;
-
-```javascript
-const { onSuccess } = useRequest(todoList, {
- //...
- async middleware(context, next) {
- // Decorate the successful callback function, the following function parameters are explained:
- // handler: bound callback function
- // event: the event object corresponding to the callback function
- // index: The subscript of the callback function, indicating which callback function is currently being executed
- // length: the number of callback functions bound
- context.decorateSuccess((handler, event, index, length) => {
- event.custom = 1;
- const received = handler(event, 'extra data');
- if (index === 1) {
- console.log(`received the return value of ${index + 1} callback function:`, received);
- // [Print information] Received the return value of the second callback function: I'm second handler
- }
- });
- //...
- }
-});
-onSuccess((event, extra) => {
- console.log(event.custom); // 1
- console.log(extra); // extra data
-});
-onSuccess((event, extra) => {
- return "I'm second handler";
-});
-```
-
-The usage of `decorateError`, `decorateComplete` is the same as `decorateSuccess`.
-
-## Abort or repeat send request
-
-In the middleware, you can also receive `abort` and `send` functions returned by use hooks (`fetch` in useFetcher), and you can also send multiple requests when triggering a request intent.
-
-A typical usage example is request retry. After sending a request, if the request fails, it will automatically request again according to a certain strategy, and `onSuccess` will be triggered after the retry is successful. The following is a sample code for a simple request retry.
-
-
-
-
-```javascript
-async function alovaFrontMiddleware(context, next) {
- return next().catch(error => {
- if (needRetry) {
- setTimeout(() => {
- context.send(...context.sendArgs);
- }, retryDelay);
- }
- return Promise.reject(error);
- });
-}
-```
-
-
-
-
-```javascript
-async function alovaFetcherMiddleware(context, next) {
- return next().catch(error => {
- if (needRetry) {
- setTimeout(() => {
- context.fetch(context.method, ...context.fetchArgs);
- }, retryDelay);
- }
- return Promise.reject(error);
- });
-}
-```
-
-
-
-
-If you need to abort the request inside the middleware, you can call `context.abort()`.
-
-## Controlled loading state
-
-In the above content, we know that you can customize and modify the responsive data through `context.update`, but when you modify the loading status value (`loading` or `fetching`), it will be hindered, because in normal circumstances Next, the loading status value will be automatically set to true when `next` is called, and false will be automatically set in the response process, which will overwrite the loading status value modified by `context.update`, at this time we can turn on the controlled loading status , after it is turned on, the `next` function and the response process will no longer modify the loading status value, but we have full control over it.
-
-Let's take request retry as an example. We hope that the loading status will remain true after the request is retried until the request ends.
-
-
-
-
-In the middleware of useRequest and useWatcher, use `context.controlLoading` to enable custom control loading status.
-
-```javascript
-async function alovaFrontMiddleware(context, next) {
- context.controlLoading();
-
- // Set to true when the request starts
- context.update({ loading: true });
- return next()
- .then(value => {
- // set to false after successful request
- context.update({ loading: false });
- return value;
- })
- .catch(error => {
- if (needRetry) {
- setTimeout(() => {
- context.send(...context.sendArgs);
- }, retryDelay);
- } else {
- // Also set to false when not retrying again
- context.update({ loading: false });
- }
- return Promise.reject(error);
- });
-}
-```
-
-
-
-
-In the middleware of useFetching, use `context.controlFetching` to enable custom control loading state.
-
-```javascript
-async function alovaFetcherMiddleware(context, next) {
- context.controlFetching();
-
- // Set to true when the request starts
- context.update({ fetching: true });
- return next()
- .then(value => {
- // set to false after successful request
- context.update({ fetching: false });
- return value;
- })
- .catch(error => {
- if (needRetry) {
- setTimeout(() => {
- context.fetch(context.method, ...context.fetchArgs);
- }, retryDelay);
- } else {
- // Also set to false when not retrying again
- context.update({ fetching: false });
- }
- return Promise.reject(error);
- });
-}
-```
-
-
-
+---
+title: Middleware
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Request middleware is an asynchronous function. it provides a powerful ability to control almost all behaviors of a request. If you just use alova, then you probably don't need to use request middleware, because it is mainly used to complete custom request strategies, no matter simple or complex request strategies, you may use it, let's look at it next What magical powers does it have.
+
+## Middleware function
+
+Request middleware is an async function, You can define request middleware in `useRequest`, `useWatcher`, `useFetcher`. The following is a simple request middleware, which prints some information before and after the request without changing any request behavior.
+
+```javascript
+useRequest(todoList, {
+ async middleware(_, next) {
+ console.log('before request');
+ await next();
+ console.log('after request');
+ }
+});
+```
+
+Here are a few things you need to know about the `next` function call. This function is also an asynchronous function. Calling it can continue to send requests. At this time, the _loading_ state will be set to true, and then the request will be sent. The return value of next is a Promise instance with the response data, you can manipulate the return value in the middleware function.
+
+## Control response data
+
+The return value of the middleware function will be used as the response data of this request to participate in subsequent processing. If the middleware does not return any data but calls `next`, the response data of this request will be used for subsequent processing.
+
+```javascript
+// The modified result will be used as the response data
+useRequest(todoList, {
+ async middleware(_, next) {
+ const result = await next();
+ result.code = 500;
+ return result;
+ }
+});
+
+// Will participate in subsequent processing with the response data of this request
+useRequest(todoList, {
+ async middleware(_, next) {
+ await next();
+ }
+});
+
+// will respond with the string abc
+useRequest(todoList, {
+ async middleware(_, next) {
+ await next();
+ return 'abc';
+ }
+});
+```
+
+There is also a special case here. When `next` is not called and there is no return value, subsequent processing will not be performed, which means that _onSuccess_, _onError_, _onComplete_ response events will not be triggered.
+
+```javascript
+useRequest(todoList, {
+ async middleware() {}
+});
+```
+
+## change request
+
+Sometimes you want to change the request. At this time, you can specify another method instance in `next`, and the information in this method will be requested when sending the request. At the same time, you can also set whether to force the request through `next` Penetrating the cache is also very simple.
+
+```javascript
+useRequest(todoList, {
+ async middleware(_, next) {
+ await next({
+ // Change the requested method instance
+ method: newMethodInstance,
+
+ // Whether to force the request this time
+ force: true
+ });
+ }
+});
+```
+
+## Control errors
+
+### Catch errors
+
+In the middleware, you can capture the request error generated in `next`, after capturing, the global `onError` hook will no longer be triggered.
+
+```javascript
+useRequest(todoList, {
+ async middleware(_, next) {
+ try {
+ await next();
+ } catch (e) {
+ console.error('Error caught', e);
+ }
+ }
+});
+```
+
+### Throw an error
+
+Of course, you can also throw a custom error in the middleware, even if the request is normal, it will enter the request error process.
+
+```javascript
+// No request is sent, and global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned
+useRequest(todoList, {
+ async middleware(_, next) {
+ throw new Error('error on before request');
+ await next();
+ }
+});
+
+// After request is success, global and request-level onError will be triggered at the same time. If the request is sent through `method.send`, the promise instance of rejection will be returned
+useRequest(todoList, {
+ async middleware(_, next) {
+ await next();
+ throw new Error('error on after request');
+ }
+});
+```
+
+## Control response delay
+
+In the middleware, we can delay the response or respond in advance. In the case of advance, although the response data cannot be obtained, some other data can be returned as the response data to participate in subsequent processing.
+
+```javascript
+// Delay response for 1 second
+useRequest(todoList, {
+ async middleware(_, next) {
+ await new Promise(resolve => {
+ setTimeout(resolve, 1000);
+ });
+ return next();
+ }
+});
+
+// Respond immediately and use the string abc as the response data
+useRequest(todoList, {
+ async middleware(_, next) {
+ return 'abc';
+ }
+});
+```
+
+## More than that
+
+**So far, all we have mentioned is the use of the second parameter `next` of the middleware, so what is the first parameter for? **
+
+The first parameter of the middleware contains some information about this request, as well as the control functions for the status and events returned in useHook such as `loading`, `data` and `onSuccess`. Let's move on!
+
+## Included request information
+
+
+
+
+The following is the request information contained in the middleware of useRequest and useWatcher
+
+```javascript
+async function alovaFrontMiddleware(context, next) {
+ // The method instance of this request
+ context.method;
+
+ // The parameter array sent by the send function, the default is []
+ context.sendArgs;
+
+ // The cache data hit by this request
+ context.cachedResponse;
+
+ // configuration collection of useHook
+ context.config;
+
+ // The various states returned by useHook, including the following attributes
+ // loading, data, error, downloading, uploading, and additional states managed by managedStates
+ context.frontStates;
+ //...
+}
+```
+
+
+
+
+The following is the request information contained in the middleware of useFetcher
+
+```javascript
+async function alovaFetcherMiddleware(context, next) {
+ // The method instance of this request
+ context.method;
+
+ // The parameter group passed in by the fetch of useFetcher, the default is []
+ context.fetchArgs;
+
+ // The cache data hit by this request
+ context.cachedResponse;
+
+ // configuration collection of useHook
+ context.config;
+
+ // The various states returned by useHook, including the following attributes
+ // fetching, error, downloading, uploading
+ context.fetchStates;
+ //...
+}
+```
+
+
+
+
+Next, let's take a look at what controls are available.
+
+## Modify responsive data
+
+Use `context.update` to modify reactive data.
+
+
+
+
+```javascript
+async function alovaFrontMiddleware(context, next) {
+ context.update({
+ // Modify the loading status to true in advance
+ loading: true,
+
+ // Modify the data value, such as setting custom initialization data
+ data: {
+ /* ... */
+ }
+ });
+ //...
+}
+```
+
+
+
+
+```javascript
+async function alovaFetcherMiddleware(context, next) {
+ context.update({
+ // Modify the fetching status to true in advance
+ fetching: true,
+
+ // Modify the value of error
+ error: new Error('custom midleware error')
+ });
+ //...
+}
+```
+
+
+
+
+## Decorate events
+
+You can also decorate _onSuccess_, _onError_, _onComplete_ callback functions in middleware to make them richer, such as changing the parameters of the callback function, or receiving the return value of the callback function to achieve more functions.
+
+You can use `decorateSuccess`, `decorateError`, `decorateComplete` functions to decorate callback functions. The following takes the success callback as an example, which is decorated in 3 places:
+
+1. Added `custom` attribute to event object;
+2. Added a second parameter to the success callback function, the value is `extra data`;
+3. Receive the value of the second success callback function and print it;
+
+```javascript
+const { onSuccess } = useRequest(todoList, {
+ //...
+ async middleware(context, next) {
+ // Decorate the successful callback function, the following function parameters are explained:
+ // handler: bound callback function
+ // event: the event object corresponding to the callback function
+ // index: The subscript of the callback function, indicating which callback function is currently being executed
+ // length: the number of callback functions bound
+ context.decorateSuccess((handler, event, index, length) => {
+ event.custom = 1;
+ const received = handler(event, 'extra data');
+ if (index === 1) {
+ console.log(`received the return value of ${index + 1} callback function:`, received);
+ // [Print information] Received the return value of the second callback function: I'm second handler
+ }
+ });
+ //...
+ }
+});
+onSuccess((event, extra) => {
+ console.log(event.custom); // 1
+ console.log(extra); // extra data
+});
+onSuccess((event, extra) => {
+ return "I'm second handler";
+});
+```
+
+The usage of `decorateError`, `decorateComplete` is the same as `decorateSuccess`.
+
+## Abort or repeat send request
+
+In the middleware, you can also receive `abort` and `send` functions returned by use hooks (`fetch` in useFetcher), and you can also send multiple requests when triggering a request intent.
+
+A typical usage example is request retry. After sending a request, if the request fails, it will automatically request again according to a certain strategy, and `onSuccess` will be triggered after the retry is successful. The following is a sample code for a simple request retry.
+
+
+
+
+```javascript
+async function alovaFrontMiddleware(context, next) {
+ return next().catch(error => {
+ if (needRetry) {
+ setTimeout(() => {
+ context.send(...context.sendArgs);
+ }, retryDelay);
+ }
+ return Promise.reject(error);
+ });
+}
+```
+
+
+
+
+```javascript
+async function alovaFetcherMiddleware(context, next) {
+ return next().catch(error => {
+ if (needRetry) {
+ setTimeout(() => {
+ context.fetch(context.method, ...context.fetchArgs);
+ }, retryDelay);
+ }
+ return Promise.reject(error);
+ });
+}
+```
+
+
+
+
+If you need to abort the request inside the middleware, you can call `context.abort()`.
+
+## Controlled loading state
+
+In the above content, we know that you can customize and modify the responsive data through `context.update`, but when you modify the loading status value (`loading` or `fetching`), it will be hindered, because in normal circumstances Next, the loading status value will be automatically set to true when `next` is called, and false will be automatically set in the response process, which will overwrite the loading status value modified by `context.update`, at this time we can turn on the controlled loading status , after it is turned on, the `next` function and the response process will no longer modify the loading status value, but we have full control over it.
+
+Let's take request retry as an example. We hope that the loading status will remain true after the request is retried until the request ends.
+
+
+
+
+In the middleware of useRequest and useWatcher, use `context.controlLoading` to enable custom control loading status.
+
+```javascript
+async function alovaFrontMiddleware(context, next) {
+ context.controlLoading();
+
+ // Set to true when the request starts
+ context.update({ loading: true });
+ return next()
+ .then(value => {
+ // set to false after successful request
+ context.update({ loading: false });
+ return value;
+ })
+ .catch(error => {
+ if (needRetry) {
+ setTimeout(() => {
+ context.send(...context.sendArgs);
+ }, retryDelay);
+ } else {
+ // Also set to false when not retrying again
+ context.update({ loading: false });
+ }
+ return Promise.reject(error);
+ });
+}
+```
+
+
+
+
+In the middleware of useFetching, use `context.controlFetching` to enable custom control loading state.
+
+```javascript
+async function alovaFetcherMiddleware(context, next) {
+ context.controlFetching();
+
+ // Set to true when the request starts
+ context.update({ fetching: true });
+ return next()
+ .then(value => {
+ // set to false after successful request
+ context.update({ fetching: false });
+ return value;
+ })
+ .catch(error => {
+ if (needRetry) {
+ setTimeout(() => {
+ context.fetch(context.method, ...context.fetchArgs);
+ }, retryDelay);
+ } else {
+ // Also set to false when not retrying again
+ context.update({ fetching: false });
+ }
+ return Promise.reject(error);
+ });
+}
+```
+
+
+
diff --git a/docs/tutorial/06-advanced/05-custom-method-key.md b/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md
similarity index 94%
rename from docs/tutorial/06-advanced/05-custom-method-key.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md
index 031585771..a4dbccb56 100644
--- a/docs/tutorial/06-advanced/05-custom-method-key.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/05-custom-method-key.md
@@ -1,26 +1,25 @@
----
-title: custom method key
-sidebar_position: 50
----
-
-:::info version required
-
-v2.20.0+
-
-:::
-
-Method key is used to identify all data associated with method instances and has a great effect, for example:
-
-- Caching of associated response data
-- Identity sharing request
- -Associate the status value returned by useRequest and other useHook
-
-By default, the method key is generated from the relevant request parameters of the method instance, which can accurately identify a request.
-
-But sometimes you want to change it so that the above three situations can be recognized as the same method in different requests.
-
-```javascript
-//The method key is generated when creating, you can customize it through __key__
-const methodInst = alovaInstance.Get('/api/user', {});
-methodInst.__key__ = 'my-custom-method-key';
-```
+---
+title: custom method key
+---
+
+:::info version required
+
+v2.20.0+
+
+:::
+
+Method key is used to identify all data associated with method instances and has a great effect, for example:
+
+- Caching of associated response data
+- Identity sharing request
+ -Associate the status value returned by useRequest and other useHook
+
+By default, the method key is generated from the relevant request parameters of the method instance, which can accurately identify a request.
+
+But sometimes you want to change it so that the above three situations can be recognized as the same method in different requests.
+
+```javascript
+//The method key is generated when creating, you can customize it through __key__
+const methodInst = alovaInstance.Get('/api/user', {});
+methodInst.__key__ = 'my-custom-method-key';
+```
diff --git a/docs/tutorial/06-advanced/06-error-logger.md b/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md
similarity index 95%
rename from docs/tutorial/06-advanced/06-error-logger.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md
index 7e96a6998..9c7b2da6a 100644
--- a/docs/tutorial/06-advanced/06-error-logger.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/06-error-logger.md
@@ -1,37 +1,36 @@
----
-title: Error logger
-sidebar_position: 60
----
-
-:::info version required
-
-v2.6.0+
-
-:::
-
-For the convenience of debugging, when using use hooks to request or respond to an error, the error logger will be printed on the console by default. If you do not want to print an error message or customize the control to print an error message in some cases (such as a production environment), alova also Support for them is provided.
-
-## Disable error logger
-
-Log printing can be turned off by setting `errorLogger` to `false or null` when creating an alova instance.
-
-```javascript
-const alovaInstance = createAlova({
- //...
- errorLogger: false
-});
-```
-
-## Custom print error logger
-
-The error logger is printed by `console.error` by default. If `console.error` is not supported in your project environment, or if you want to collect error information, you can specify `errorLogger` as a function to customize error logger.
-
-```javascript
-const alovaInstance = createAlova({
- //...
- // error is the error instance, methodInstance is the method instance corresponding to the error
- errorLogger(error, methodInstance) {
- reportError(`${methodInstance.url}: ${error.message}`);
- }
-});
-```
+---
+title: Error logger
+---
+
+:::info version required
+
+v2.6.0+
+
+:::
+
+For the convenience of debugging, when using use hooks to request or respond to an error, the error logger will be printed on the console by default. If you do not want to print an error message or customize the control to print an error message in some cases (such as a production environment), alova also Support for them is provided.
+
+## Disable error logger
+
+Log printing can be turned off by setting `errorLogger` to `false or null` when creating an alova instance.
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ errorLogger: false
+});
+```
+
+## Custom print error logger
+
+The error logger is printed by `console.error` by default. If `console.error` is not supported in your project environment, or if you want to collect error information, you can specify `errorLogger` as a function to customize error logger.
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ // error is the error instance, methodInstance is the method instance corresponding to the error
+ errorLogger(error, methodInstance) {
+ reportError(`${methodInstance.url}: ${error.message}`);
+ }
+});
+```
diff --git a/docs/tutorial/06-advanced/07-cache-logger.md b/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md
similarity index 95%
rename from docs/tutorial/06-advanced/07-cache-logger.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md
index 311c0d67a..afc6086e9 100644
--- a/docs/tutorial/06-advanced/07-cache-logger.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/07-cache-logger.md
@@ -1,60 +1,59 @@
----
-title: cache logger
-sidebar_position: 70
----
-
-:::info version required
-
-v2.8.0+
-
-:::
-
-In order to facilitate debugging when using the interface cache, when the request hits the cache without sending a network request, the hit cache information will be printed on the console by default, which can solve some confusion when using the cache.
-
-If you don't want to print cache information or custom control print cache information in some cases (such as production environment), alova also provides support for them.
-
-## Close cache logger printing
-
-Console printing can be turned off by setting `cacheLogger` to `false or null` when creating an alova instance.
-
-```javascript
-const alovaInstance = createAlova({
- //...
- cacheLogger: false
-});
-```
-
-You can also dynamically turn it on and off according to different environments.
-
-```javascript
-const alovaInstance = createAlova({
- //...
- // Enable cache logger in the development environment
- cacheLogger: process.env.NODE_ENV === 'development'
-});
-```
-
-## Custom print cache logger
-
-The cache logger is printed via `console.log` by default. If `console.log` or other purposes are not supported in your project environment, `cacheLogger` can be specified as a function to customize the logger for processing cache hits.
-
-```javascript
-const alovaInstance = createAlova({
- //...
- /**
- * Custom cache logger function
- * @param response hit cache data
- * @param method the current method instance
- * @param cacheMode cache mode memory or restore
- * @param tag The tag in the restore mode has a value only when the tag is set in the corresponding cache
- */
- cacheLogger(response, method, cacheMode, tag) {
- saveHitCache({
- response,
- method,
- cacheMode,
- tag
- });
- }
-});
-```
+---
+title: cache logger
+---
+
+:::info version required
+
+v2.8.0+
+
+:::
+
+In order to facilitate debugging when using the interface cache, when the request hits the cache without sending a network request, the hit cache information will be printed on the console by default, which can solve some confusion when using the cache.
+
+If you don't want to print cache information or custom control print cache information in some cases (such as production environment), alova also provides support for them.
+
+## Close cache logger printing
+
+Console printing can be turned off by setting `cacheLogger` to `false or null` when creating an alova instance.
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ cacheLogger: false
+});
+```
+
+You can also dynamically turn it on and off according to different environments.
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ // Enable cache logger in the development environment
+ cacheLogger: process.env.NODE_ENV === 'development'
+});
+```
+
+## Custom print cache logger
+
+The cache logger is printed via `console.log` by default. If `console.log` or other purposes are not supported in your project environment, `cacheLogger` can be specified as a function to customize the logger for processing cache hits.
+
+```javascript
+const alovaInstance = createAlova({
+ //...
+ /**
+ * Custom cache logger function
+ * @param response hit cache data
+ * @param method the current method instance
+ * @param cacheMode cache mode memory or restore
+ * @param tag The tag in the restore mode has a value only when the tag is set in the corresponding cache
+ */
+ cacheLogger(response, method, cacheMode, tag) {
+ saveHitCache({
+ response,
+ method,
+ cacheMode,
+ tag
+ });
+ }
+});
+```
diff --git a/docs/tutorial/06-advanced/08-manage-extra-states.md b/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md
similarity index 95%
rename from docs/tutorial/06-advanced/08-manage-extra-states.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md
index 8de90c559..02eb05adc 100644
--- a/docs/tutorial/06-advanced/08-manage-extra-states.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/08-manage-extra-states.md
@@ -1,198 +1,197 @@
----
-title: Manage extra states
-sidebar_position: 80
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-In the previous [Cross Page/Module Update Response States](/tutorial/advanced/update-across-components) chapter, we introduced how to update the response status across pages or modules, but in this chapter we It only introduces updating the `data` state returned by `useRequest` and `useWatcher` through `updateState`, the value of data is always consistent with the response data, but in many cases we will use additional states(such as state A) to display data, and After the request is successful, the data data is appended to the additional state A, such as the pagination scheme of pull-down loading. In this case, we need to manage the additional state A so that it can be updated across pages/modules.
-
-## Update a single state
-
-Additional states can be managed via `managedStates` when called by the use hook, and automatically assigned the state name to update it when `updateState` is called in other modules/pages.
-
-
-
-
-```javascript title="A.vue"
-const todoList = page =>
- alova.Get('/todo', {
- name: 'todoList'
- });
-
-const allTodo = ref([]);
-useRequest(todoList, {
- //...
-
- // highlight-start
- // manage allTodo as additional state
- managedStates: {
- allTodo
- }
- // highlight-end
-});
-```
-
-```javascript title="B.vue"
-const handleSuccess = () => {
- // highlight-start
- // Pass in an object and specify the state name to look up
- updateState('todoList', {
- allTodo: allTodoData => {
- // Add a new todo item
- allTodoData.push({
- title: 'new todo',
- time: '10:00'
- });
- return allTodoData;
- }
- });
- // highlight-end
-};
-```
-
-
-
-
-
-```javascript title="A.jsx"
-const PageA = () => {
- const todoList = page =>
- alova.Get('/todo', {
- name: 'todoList'
- });
-
- const [allTodo, setAllTodo] = allTodoState = useState([]);
- useRequest(todoList, {
- //...
-
- // highlight-start
- // manage allTodo as additional state
- managedStates: {
- allTodo: allTodoState
- }
- // highlight-end
- });
-
- return (
- //...
- );
-}
-```
-
-```javascript title="B.jsx"
-const PageB = () => {
- //...
- const handleSuccess = () => {
- // highlight-start
- // Pass in an object and specify the state name to look up
- updateState('todoList', {
- allTodo: allTodoData => {
- // Add a new todo item
- allTodoData.push({
- title: 'new todo',
- time: '10:00'
- });
- return allTodoData;
- }
- });
- // highlight-end
- };
-
- return (
- //...
- );
-}
-```
-
-
-
-
-
-```javascript title="A.svelte"
-const todoList = page =>
- alova.Get('/todo', {
- name: 'todoList'
- });
-
-const allTodo = ref([]);
-useRequest(todoList, {
- //...
-
- // highlight-start
- // manage allTodo as additional state
- managedStates: {
- allTodo
- }
- // highlight-end
-});
-```
-
-```javascript title="B.svelte"
-const handleSuccess = () => {
- // highlight-start
- // Pass in an object and specify the state name to look up
- updateState('todoList', {
- allTodo: allTodoData => {
- // Add a new todo item
- allTodoData.push({
- title: 'new todo',
- time: '10:00'
- });
- return allTodoData;
- }
- });
- // highlight-end
-};
-```
-
-
-
-
-:::info Note
-
-Not support to manage additional states.
-
-:::
-
-
-
-
-## Update multiple states
-
-In the above example, we implemented the update of a single `allTodo` state across pages. In fact, any number of states can be updated at the same time through the object description method of `updateState`.
-
-```javascript
-updateState('todoList', {
- state1: state1Data => {
- //...
- },
- state2: state2Data => {
- //...
- },
- state3: state3Data => {
- //...
- }
- //...
-});
-```
-
-It should be noted that the above 3 additional states need to be managed through the `managedStates` property before updating.
-
-## shorthand for data status update
-
-When only updating the data state, you can directly pass in the callback function instead of specifying it as an object.
-
-```javascript
-updateState('todoList', {
- data: dataRaw => {
- //...
- }
-});
-
-// The following are shorthand
-updateState('todoList', dataRaw => {
- //...
-});
-```
+---
+title: Manage extra states
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+In the previous [Cross Page/Module Update Response States](/tutorial/advanced/update-across-components) chapter, we introduced how to update the response status across pages or modules, but in this chapter we It only introduces updating the `data` state returned by `useRequest` and `useWatcher` through `updateState`, the value of data is always consistent with the response data, but in many cases we will use additional states(such as state A) to display data, and After the request is successful, the data data is appended to the additional state A, such as the pagination scheme of pull-down loading. In this case, we need to manage the additional state A so that it can be updated across pages/modules.
+
+## Update a single state
+
+Additional states can be managed via `managedStates` when called by the use hook, and automatically assigned the state name to update it when `updateState` is called in other modules/pages.
+
+
+
+
+```javascript title="A.vue"
+const todoList = page =>
+ alova.Get('/todo', {
+ name: 'todoList'
+ });
+
+const allTodo = ref([]);
+useRequest(todoList, {
+ //...
+
+ // highlight-start
+ // manage allTodo as additional state
+ managedStates: {
+ allTodo
+ }
+ // highlight-end
+});
+```
+
+```javascript title="B.vue"
+const handleSuccess = () => {
+ // highlight-start
+ // Pass in an object and specify the state name to look up
+ updateState('todoList', {
+ allTodo: allTodoData => {
+ // Add a new todo item
+ allTodoData.push({
+ title: 'new todo',
+ time: '10:00'
+ });
+ return allTodoData;
+ }
+ });
+ // highlight-end
+};
+```
+
+
+
+
+
+```javascript title="A.jsx"
+const PageA = () => {
+ const todoList = page =>
+ alova.Get('/todo', {
+ name: 'todoList'
+ });
+
+ const [allTodo, setAllTodo] = allTodoState = useState([]);
+ useRequest(todoList, {
+ //...
+
+ // highlight-start
+ // manage allTodo as additional state
+ managedStates: {
+ allTodo: allTodoState
+ }
+ // highlight-end
+ });
+
+ return (
+ //...
+ );
+}
+```
+
+```javascript title="B.jsx"
+const PageB = () => {
+ //...
+ const handleSuccess = () => {
+ // highlight-start
+ // Pass in an object and specify the state name to look up
+ updateState('todoList', {
+ allTodo: allTodoData => {
+ // Add a new todo item
+ allTodoData.push({
+ title: 'new todo',
+ time: '10:00'
+ });
+ return allTodoData;
+ }
+ });
+ // highlight-end
+ };
+
+ return (
+ //...
+ );
+}
+```
+
+
+
+
+
+```javascript title="A.svelte"
+const todoList = page =>
+ alova.Get('/todo', {
+ name: 'todoList'
+ });
+
+const allTodo = ref([]);
+useRequest(todoList, {
+ //...
+
+ // highlight-start
+ // manage allTodo as additional state
+ managedStates: {
+ allTodo
+ }
+ // highlight-end
+});
+```
+
+```javascript title="B.svelte"
+const handleSuccess = () => {
+ // highlight-start
+ // Pass in an object and specify the state name to look up
+ updateState('todoList', {
+ allTodo: allTodoData => {
+ // Add a new todo item
+ allTodoData.push({
+ title: 'new todo',
+ time: '10:00'
+ });
+ return allTodoData;
+ }
+ });
+ // highlight-end
+};
+```
+
+
+
+
+:::info Note
+
+Not support to manage additional states.
+
+:::
+
+
+
+
+## Update multiple states
+
+In the above example, we implemented the update of a single `allTodo` state across pages. In fact, any number of states can be updated at the same time through the object description method of `updateState`.
+
+```javascript
+updateState('todoList', {
+ state1: state1Data => {
+ //...
+ },
+ state2: state2Data => {
+ //...
+ },
+ state3: state3Data => {
+ //...
+ }
+ //...
+});
+```
+
+It should be noted that the above 3 additional states need to be managed through the `managedStates` property before updating.
+
+## shorthand for data status update
+
+When only updating the data state, you can directly pass in the callback function instead of specifying it as an object.
+
+```javascript
+updateState('todoList', {
+ data: dataRaw => {
+ //...
+ }
+});
+
+// The following are shorthand
+updateState('todoList', dataRaw => {
+ //...
+});
+```
diff --git a/docs/tutorial/06-advanced/09-ssr.md b/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md
similarity index 96%
rename from docs/tutorial/06-advanced/09-ssr.md
rename to versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md
index e2a09ab2e..7ab3625b6 100644
--- a/docs/tutorial/06-advanced/09-ssr.md
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/09-ssr.md
@@ -1,228 +1,227 @@
----
-title: Server-Side Rendering(SSR)
-sidebar_position: 90
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info version required
-
-2.8.0+
-
-:::
-
-## Overview
-
-Although the positioning of alova is not to make requests in nodejs, we have also adapted it in order to combine the server-side rendering([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)) of the UI framework. Although built-in request functionality is provided in e.g. `Nuxt3.x`, `Sveltekit`, if you choose to use alova, you can use alova to manage requests in both server and client, instead of server and client separately. Use different request schemes to manage them.
-
-Here are some caveats for using alova in SSR, and examples of usage in SSR for different UI frameworks.
-
-## Call apis on server
-
-In SSR, it is necessary to get data on server and render it into HTML. In this case, we cannot use alova's use hooks (and do not need to use them) to obtain data. Below we will show the supported SSR frameworks respectively.
-
-### Nuxt3.x
-
-In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `useFetch` and `$fetch` request functions are also provided. These request functions that can be used on both server and client are really convenient. However, if you want to use alova in nuxt, you can use the combination of **useAsyncData + alova.Method** to complete the server-side data fetching, which is no different from your usual `useAsyncData`.
-
-```html
-
-```
-
-### Nextjs
-
-Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function.
-
-```jsx
-const todoListGetter = alovaInstance.Get('/todo/list', {
- headers: {
- 'Content-Type': 'application/json;charset=UTF-8'
- }
-});
-
-export const getServerSideProps = async ctx => {
- const list = await todoListGetter.send();
- return {
- props: {
- list
- }
- };
-};
-export default function App(props) {
- return props.list.map(item => (
-
- {item.title}
- {item.time}
-
- ));
-}
-```
-
-### Sveltekit
-
-Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`.
-
-```javascript title=+page.server.js
-const todoListGetter = alovaInstance.Get('/todo/list', {
- headers: {
- 'Content-Type': 'application/json;charset=UTF-8'
- }
-});
-
-/** @type {import('./$types').PageServerLoad} */
-export async function load({ params }) {
- return {
- list: todoListGetter.send()
- };
-}
-```
-
-## Using usehooks in SSR
-
-Since each SSR framework has its own way to initialize data on server, when generating html in SSR, `useRequest` and `useWatcher` in the component will not be initiated even if `immediate` is set to `true` request, as this is more like client initialization data.
-
-However, if you need to initialize the data of the page as in the client, you can also set `immediate` to `true`, and when the page is running in the browser, you can use all the functions of alova as usual.
-
-## Precautions
-
-### Client and server caches are separate
-
-If you use alova's caching function, you may need to pay attention here that the client and server caches are not shared, which means that if you directly use **usehooks** to get data when initializing the page, you may Run into inconsistencies in client-side and server-side rendering, although few people do.
-
-Please see the following code snippet.
-
-
-
-
-```html
-
-
-```
-
-
-
-
-The following code assumes that `alovaGetter` requests are cached on server, but not on the client.
-
-At this time, when the html is generated on server , `loading` is `false` and does not display `
loading
` because it hits the cache, but when the client is initialized, because it misses the cache, `loading` is `true` will cause `
loading
` to be displayed, and the SSR framework will prompt that the rendering of the two ends is inconsistent.
-
-**Solution**
-
-1. Try to put the page data initialization work in the acquisition function instead of the component;
-2. If you must do this, you can avoid using the same apis on the client and server, or turn off the problematic api caches;
-3. If caching is also required, you can clear the cache on server in the data initialization function of server. The sample code is as follows:
-
-
-
-
-```html
-
-
- >
- );
-}
-
-export const getServerSideProps = async () => {
- // Clear the cache on server
- invalidateCache(alovaGetter);
- return {
- props: {}
- };
-};
-```
-
-
-
-
-```javascript title=+page.server.js
-import { invalidateCache } from 'alova';
-
-/** @type {import('./$types').PageServerLoad} */
-export async function load({ params }) {
- // Clear the cache on server
- invalidateCache(alovaGetter);
- return {};
-}
-```
-
-
-
+---
+title: Server-Side Rendering(SSR)
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info version required
+
+2.8.0+
+
+:::
+
+## Overview
+
+Although the positioning of alova is not to make requests in nodejs, we have also adapted it in order to combine the server-side rendering([Nuxt3.x](https://nuxt.com/) / [Nextjs](https://nextjs.org/) / [sveltekit](https://kit.svelte.dev/)) of the UI framework. Although built-in request functionality is provided in e.g. `Nuxt3.x`, `Sveltekit`, if you choose to use alova, you can use alova to manage requests in both server and client, instead of server and client separately. Use different request schemes to manage them.
+
+Here are some caveats for using alova in SSR, and examples of usage in SSR for different UI frameworks.
+
+## Call apis on server
+
+In SSR, it is necessary to get data on server and render it into HTML. In this case, we cannot use alova's use hooks (and do not need to use them) to obtain data. Below we will show the supported SSR frameworks respectively.
+
+### Nuxt3.x
+
+In Nuxt3.x, `useAsyncData` is provided to initialize page data on server, and `useFetch` and `$fetch` request functions are also provided. These request functions that can be used on both server and client are really convenient. However, if you want to use alova in nuxt, you can use the combination of **useAsyncData + alova.Method** to complete the server-side data fetching, which is no different from your usual `useAsyncData`.
+
+```html
+
+```
+
+### Nextjs
+
+Nextjs provides fixed server-side initialization page data functions, such as `getStaticProps`, `getServerSideProps`, etc., you can [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function.
+
+```jsx
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ }
+});
+
+export const getServerSideProps = async ctx => {
+ const list = await todoListGetter.send();
+ return {
+ props: {
+ list
+ }
+ };
+};
+export default function App(props) {
+ return props.list.map(item => (
+
+ {item.title}
+ {item.time}
+
+ ));
+}
+```
+
+### Sveltekit
+
+Sveltekit also provides the `load` function to initialize the page data on server, and you can also [directly use the method instance](/tutorial/getting-started/quick-start) call apis in the function. For example, call apis in `+page.server.js`.
+
+```javascript title=+page.server.js
+const todoListGetter = alovaInstance.Get('/todo/list', {
+ headers: {
+ 'Content-Type': 'application/json;charset=UTF-8'
+ }
+});
+
+/** @type {import('./$types').PageServerLoad} */
+export async function load({ params }) {
+ return {
+ list: todoListGetter.send()
+ };
+}
+```
+
+## Using usehooks in SSR
+
+Since each SSR framework has its own way to initialize data on server, when generating html in SSR, `useRequest` and `useWatcher` in the component will not be initiated even if `immediate` is set to `true` request, as this is more like client initialization data.
+
+However, if you need to initialize the data of the page as in the client, you can also set `immediate` to `true`, and when the page is running in the browser, you can use all the functions of alova as usual.
+
+## Precautions
+
+### Client and server caches are separate
+
+If you use alova's caching function, you may need to pay attention here that the client and server caches are not shared, which means that if you directly use **usehooks** to get data when initializing the page, you may Run into inconsistencies in client-side and server-side rendering, although few people do.
+
+Please see the following code snippet.
+
+
+
+
+```html
+
+
+```
+
+
+
+
+The following code assumes that `alovaGetter` requests are cached on server, but not on the client.
+
+At this time, when the html is generated on server , `loading` is `false` and does not display `
loading
` because it hits the cache, but when the client is initialized, because it misses the cache, `loading` is `true` will cause `
loading
` to be displayed, and the SSR framework will prompt that the rendering of the two ends is inconsistent.
+
+**Solution**
+
+1. Try to put the page data initialization work in the acquisition function instead of the component;
+2. If you must do this, you can avoid using the same apis on the client and server, or turn off the problematic api caches;
+3. If caching is also required, you can clear the cache on server in the data initialization function of server. The sample code is as follows:
+
+
+
+
+```html
+
+
+ >
+ );
+}
+
+export const getServerSideProps = async () => {
+ // Clear the cache on server
+ invalidateCache(alovaGetter);
+ return {
+ props: {}
+ };
+};
+```
+
+
+
+
+```javascript title=+page.server.js
+import { invalidateCache } from 'alova';
+
+/** @type {import('./$types').PageServerLoad} */
+export async function load({ params }) {
+ // Clear the cache on server
+ invalidateCache(alovaGetter);
+ return {};
+}
+```
+
+
+
diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/README.md b/versioned_docs/version-2.x/tutorial/06-advanced/README.md
new file mode 100644
index 000000000..feaeb352d
--- /dev/null
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/README.md
@@ -0,0 +1,9 @@
+---
+title: 进阶
+---
+
+import DocCardList from '@theme/DocCardList';
+
+进阶教程可以让你更深入地了解 alova 的一些特性,它们可能并不是常用功能,但可以帮你快速解决更多特殊的请求问题。
+
+
diff --git a/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json b/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json
new file mode 100644
index 000000000..aa8953a08
--- /dev/null
+++ b/versioned_docs/version-2.x/tutorial/06-advanced/_category_.json
@@ -0,0 +1,3 @@
+{
+ "label": "Advanced"
+}
diff --git a/docs/tutorial/07-best-practice/01-manage-apis.md b/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md
similarity index 96%
rename from docs/tutorial/07-best-practice/01-manage-apis.md
rename to versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md
index 54127698e..97e6be845 100644
--- a/docs/tutorial/07-best-practice/01-manage-apis.md
+++ b/versioned_docs/version-2.x/tutorial/07-best-practice/01-manage-apis.md
@@ -1,154 +1,153 @@
----
-title: Manage APIs
-sidebar_position: 10
----
-
-In a project, we may need to use hundreds or thousands of request APIs, so managing these request APIs becomes particularly important.
-
-You may write the request code like the code snippet in [quick start](/tutorial/getting-started/quick-start). all codes in one file.
-
-```javascript
-const { loading, data, error } = useRequest(
- alovaInstance.Get('https://api.alovajs.org/profile', {
- params: {
- id: 1
- }
- })
-);
-```
-
-This is just for beginners to understand, but in actual projects, we do not recommend this, because the method instance is not only used to send requests, it can also be used to operate cache and state, the above usage will make these request api become It's unmanageable, and if you think it's wrong, you might forget a little:
-
-> The key of the response data cache is uniquely identified by the combination of the method instance’s request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody). Or different positions will be treated as different keys.
-
-Therefore, in actual projects, method instances should be managed, and alova instances can also be managed uniformly.
-
-## api file structure
-
-First of all, your project needs a folder that uniformly stores method instances and alova instances, for example called `api`, the following is a common api management structure, and you can also use any structure suitable for the project.
-
-```
-|-api
-| |-index.js -> contains all alova instances
-| |-methods
-| | |-user.js
-| | |-article.js
-| | |-order.js
-| | |-...
-|-...
-```
-
-In short, your project should use a suitable folder structure to organize them.
-
-> Next, take vue as an example to show the sample code
-
-## Manage alova instance
-
-Your project may need to communicate with different servers, or you may need to use special request schemes in specific requests, or use different response interceptors, etc. All of these require creating and maintaining multiple alova instances in the project. It is recommended to Use a separate file to manage them, for example in the above api management structure, will use `api/index.js` to manage.
-
-```javascript title=api/index.js
-import { createAlova } from 'alova';
-import VueHook from 'alova/vue';
-import GlobalFetch from 'alova/GlobalFetch';
-import { axiosRequestAdapter } from '@alova/adapter-axios';
-
-// user alova instance
-export const userAlova = createAlova({
- baseURL: 'https://api-user.alovajs.org',
- statesHook: VueHook,
- requestAdapter: GlobalFetch(),
- async responded(method) {
- method.config.headers.token = 'user token';
- }
-});
-
-// order alova instance
-export const orderAlova = createAlova({
- baseURL: 'https://api-order.alovajs.org',
- statesHook: VueHook,
- requestAdapter: GlobalFetch(),
- async responded(method) {
- method.config.headers.token = 'order token';
- }
-});
-
-// upload alova instance
-export const uploadAlova = createAlova({
- baseURL: 'https://api-order.alovajs.org',
- statesHook: VueHook,
- requestAdapter: axiosRequestAdapter()
-});
-```
-
-## Manage method instances
-
-We can use different js files to classify and manage method instances. For example, in the above api management structure, `api/methods/user.js` will be used to manage method instances related to user information, and `api/methods/order.js` will be used `A method instance related to order management.
-
-In addition, as mentioned above, in addition to sending requests, method instances can also be used to operate caches and states. In order to ensure the number and order of request parameters, we can use a function to correspond to a request API, through The corresponding method instance is returned in the form of incoming request parameters. As long as the incoming parameters are the same, the request information and parameter order of the method instance are also the same, so as to ensure that the method instance used to operate the cache and state is correct.
-
-```javascript title=api/methods/user.js
-import { userAlova } from '..';
-
-// Get user information
-export const getUserInfo = id => userAlova.Get('/user/' + id);
-
-// Edit user information
-export const editUserInfo = (name, age, mobile) =>
- userAlova.Post('/user', {
- name,
- age,
- mobile
- });
-
-// remove user
-export const removeUser = id => userAlova.Delete('/user/' + id);
-
-//...
-```
-
-In the **user component**, the method function can be directly imported for use, and the method function can be used again to invalidate the corresponding cache after calling `invalidateCache`.
-
-```html title=views/user.vue
-
-
Loading...
-
{{ error.message }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
+---
+title: Manage APIs
+---
+
+In a project, we may need to use hundreds or thousands of request APIs, so managing these request APIs becomes particularly important.
+
+You may write the request code like the code snippet in [quick start](/tutorial/getting-started/quick-start). all codes in one file.
+
+```javascript
+const { loading, data, error } = useRequest(
+ alovaInstance.Get('https://api.alovajs.org/profile', {
+ params: {
+ id: 1
+ }
+ })
+);
+```
+
+This is just for beginners to understand, but in actual projects, we do not recommend this, because the method instance is not only used to send requests, it can also be used to operate cache and state, the above usage will make these request api become It's unmanageable, and if you think it's wrong, you might forget a little:
+
+> The key of the response data cache is uniquely identified by the combination of the method instance’s request method (method), request address (url), request header parameters (headers), url parameters (params), and request body parameters (requestBody). Or different positions will be treated as different keys.
+
+Therefore, in actual projects, method instances should be managed, and alova instances can also be managed uniformly.
+
+## api file structure
+
+First of all, your project needs a folder that uniformly stores method instances and alova instances, for example called `api`, the following is a common api management structure, and you can also use any structure suitable for the project.
+
+```
+|-api
+| |-index.js -> contains all alova instances
+| |-methods
+| | |-user.js
+| | |-article.js
+| | |-order.js
+| | |-...
+|-...
+```
+
+In short, your project should use a suitable folder structure to organize them.
+
+> Next, take vue as an example to show the sample code
+
+## Manage alova instance
+
+Your project may need to communicate with different servers, or you may need to use special request schemes in specific requests, or use different response interceptors, etc. All of these require creating and maintaining multiple alova instances in the project. It is recommended to Use a separate file to manage them, for example in the above api management structure, will use `api/index.js` to manage.
+
+```javascript title=api/index.js
+import { createAlova } from 'alova';
+import VueHook from 'alova/vue';
+import GlobalFetch from 'alova/GlobalFetch';
+import { axiosRequestAdapter } from '@alova/adapter-axios';
+
+// user alova instance
+export const userAlova = createAlova({
+ baseURL: 'https://api-user.alovajs.org',
+ statesHook: VueHook,
+ requestAdapter: GlobalFetch(),
+ async responded(method) {
+ method.config.headers.token = 'user token';
+ }
+});
+
+// order alova instance
+export const orderAlova = createAlova({
+ baseURL: 'https://api-order.alovajs.org',
+ statesHook: VueHook,
+ requestAdapter: GlobalFetch(),
+ async responded(method) {
+ method.config.headers.token = 'order token';
+ }
+});
+
+// upload alova instance
+export const uploadAlova = createAlova({
+ baseURL: 'https://api-order.alovajs.org',
+ statesHook: VueHook,
+ requestAdapter: axiosRequestAdapter()
+});
+```
+
+## Manage method instances
+
+We can use different js files to classify and manage method instances. For example, in the above api management structure, `api/methods/user.js` will be used to manage method instances related to user information, and `api/methods/order.js` will be used `A method instance related to order management.
+
+In addition, as mentioned above, in addition to sending requests, method instances can also be used to operate caches and states. In order to ensure the number and order of request parameters, we can use a function to correspond to a request API, through The corresponding method instance is returned in the form of incoming request parameters. As long as the incoming parameters are the same, the request information and parameter order of the method instance are also the same, so as to ensure that the method instance used to operate the cache and state is correct.
+
+```javascript title=api/methods/user.js
+import { userAlova } from '..';
+
+// Get user information
+export const getUserInfo = id => userAlova.Get('/user/' + id);
+
+// Edit user information
+export const editUserInfo = (name, age, mobile) =>
+ userAlova.Post('/user', {
+ name,
+ age,
+ mobile
+ });
+
+// remove user
+export const removeUser = id => userAlova.Delete('/user/' + id);
+
+//...
+```
+
+In the **user component**, the method function can be directly imported for use, and the method function can be used again to invalidate the corresponding cache after calling `invalidateCache`.
+
+```html title=views/user.vue
+
+
Loading...
+
{{ error.message }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
diff --git a/docs/tutorial/07-best-practice/02-skills.md b/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md
similarity index 96%
rename from docs/tutorial/07-best-practice/02-skills.md
rename to versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md
index ab20c258e..30c4bff52 100644
--- a/docs/tutorial/07-best-practice/02-skills.md
+++ b/versioned_docs/version-2.x/tutorial/07-best-practice/02-skills.md
@@ -1,6 +1,5 @@
---
title: Skills
-sidebar_position: 20
---
import Tabs from '@theme/Tabs';
@@ -259,8 +258,16 @@ But such a request only applies to simple parallel requests. If you need to perf
Manually create a promise object and use `Promise.all` to complete the effect.
```javascript
-const { data: todoList, onSuccess: onListSuccess, onError: onListError } = useRequest(todoListGetter);
-const { data: todoCounter, onSuccess: onCountSuccess, onError: onCountError } = useRequest(todoCountGetter);
+const {
+ data: todoList,
+ onSuccess: onListSuccess,
+ onError: onListError
+} = useRequest(todoListGetter);
+const {
+ data: todoCounter,
+ onSuccess: onCountSuccess,
+ onError: onCountError
+} = useRequest(todoCountGetter);
// Manually create promise object
const listPromise = new Promise((resolve, reject) => {
@@ -302,7 +309,10 @@ Let the first request be sent automatically, and the second request be triggered
```javascript
//
const { data: todoList, onSuccess } = useRequest(todoListGetter);
-const { data: todoDetail, send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false });
+const { data: todoDetail, send: sendTodoDetail } = useRequest(
+ todoId => todoDetailGetter(todoId),
+ { immediate: false }
+);
// Get the list first, then get the details of the first todo
onSuccess(event => {
@@ -317,7 +327,9 @@ Using the `send` function returned by the `useRequest` function, calling `send`
```javascript
// Let them not automatically send requests first
const { send: sendList } = useRequest(todoListGetter, { immediate: false });
-const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), { immediate: false });
+const { send: sendTodoDetail } = useRequest(todoId => todoDetailGetter(todoId), {
+ immediate: false
+});
//Use the promise object returned by the send function
const serialRequest = async () => {
diff --git a/docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md b/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md
similarity index 96%
rename from docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md
rename to versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md
index c3cc9a96a..33678bd7a 100644
--- a/docs/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md
+++ b/versioned_docs/version-2.x/tutorial/07-best-practice/03-manage-cache-by-indexeddb.md
@@ -1,123 +1,122 @@
----
-title: Manage Cache with IndexedDB
-sidebar_position: 30
----
-
-If you are developing applications that require extensive use of local cache, such as graphics editing applications, file management applications, etc., the low-capacity localStorage can no longer meet the development needs. At this time, you can use IndexedDB and alova for large-capacity local cache management.
-
-This feature is mainly due to alova's [Controlled Cache](/tutorial/cache/controlled-cache) function, which can implement custom cache management. Let's take a look at the practical steps.
-
-Here is an [example of managed cache with IndexedDB](/tutorial/example/controlled-cache-by-indexeddb)
-
-Let's take custom management of large image data as an example.
-
-## Create IndexedDB instance
-
-First create an IndexedDB instance to operate the local cache, and export the cache operate function.
-
-```javascript title=db.js
-const dbVersion = 1;
-let dbInstance;
-const request = window.indexedDB.open('MyTestDatabase', dbVersion);
-request.onupgradeneeded = ({ target }) => {
- dbInstance = target.result;
- const imgStore = dbInstance.createObjectStore('images', {
- autoIncrement: true
- });
- imgStore.createIndex('fileName', 'fileName', {
- unique: true
- });
-};
-request.onerror = () => {
- throw new Error('Database open fail');
-};
-request.onsuccess = ({ target }) => {
- dbInstance = target.result;
-};
-
-// Add new data to IndexedDB
-export const addImage2Cache = async (fileName, data) => {
- const tx = dbInstance.transaction(['images'], 'readwrite');
- const request = tx.objectStore('images').add({
- fileName,
- data
- });
- return new Promise((resolve, reject) => {
- request.onerror = () => {
- reject('data add fail');
- };
- request.onsuccess = ({ result }) => {
- resolve(result);
- };
- });
-};
-
-// Get file data according to fileName
-export const getImageFromCache = async fileName => {
- const tx = dbInstance.transaction(['images']);
- const request = tx.objectStore('images').index('fileName').get(fileName);
- return new Promise((resolve, reject) => {
- request.onerror = () => {
- reject('data add fail');
- };
- request.onsuccess = ({ target }) => {
- resolve(target.result);
- };
- });
-};
-```
-
-## save data
-
-When saving data, we can save the cache in the `transformData` of the method, because `transformData` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data.
-
-```javascript-api.js
-import { addImage2Cache } from './db';
-
-export const image = fileName =>
- alovaInst.Get(`/image/${fileName}`, {
- // highlight-start
- async transformData(imgBlob) {
- // Asynchronously convert the blob to base64
- const reader = new FileReader();
- reader.readAsDataURL(imgBlob);
- const base64Img = await new Promise(resolve => {
- reader.onload = ({ target }) => {
- resolve(target.result);
- };
- });
-
- // Cache image data to IndexedDB
- await addImage2Cache(fileName, base64Img);
- return base64Img;
- }
- // highlight-end
- });
-```
-
-## retrieve data
-
-Specify `localCache` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data.
-
-```javascript title=api.js
-import { getImageFromCache } from './db';
-
-export const image = fileName =>
- alovaInst.Get(`/image/${fileName}`, {
- async transformData(imgBlob) {
- //...
- },
-
- // highlight-start
- async localCache() {
- // get cache
- const cache = await getImageFromCache(fileName);
- return cache && cache.data;
- }
- // highlight-end
- });
-```
-
-In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `localCache`, so as to realize the cache expiration function.
-
-IndexedDB is just one example of managing caches asynchronously, you can also connect to your cache servers to manage them.
+---
+title: Manage Cache with IndexedDB
+---
+
+If you are developing applications that require extensive use of local cache, such as graphics editing applications, file management applications, etc., the low-capacity localStorage can no longer meet the development needs. At this time, you can use IndexedDB and alova for large-capacity local cache management.
+
+This feature is mainly due to alova's [Controlled Cache](/tutorial/cache/controlled-cache) function, which can implement custom cache management. Let's take a look at the practical steps.
+
+Here is an [example of managed cache with IndexedDB](/tutorial/example/controlled-cache-by-indexeddb)
+
+Let's take custom management of large image data as an example.
+
+## Create IndexedDB instance
+
+First create an IndexedDB instance to operate the local cache, and export the cache operate function.
+
+```javascript title=db.js
+const dbVersion = 1;
+let dbInstance;
+const request = window.indexedDB.open('MyTestDatabase', dbVersion);
+request.onupgradeneeded = ({ target }) => {
+ dbInstance = target.result;
+ const imgStore = dbInstance.createObjectStore('images', {
+ autoIncrement: true
+ });
+ imgStore.createIndex('fileName', 'fileName', {
+ unique: true
+ });
+};
+request.onerror = () => {
+ throw new Error('Database open fail');
+};
+request.onsuccess = ({ target }) => {
+ dbInstance = target.result;
+};
+
+// Add new data to IndexedDB
+export const addImage2Cache = async (fileName, data) => {
+ const tx = dbInstance.transaction(['images'], 'readwrite');
+ const request = tx.objectStore('images').add({
+ fileName,
+ data
+ });
+ return new Promise((resolve, reject) => {
+ request.onerror = () => {
+ reject('data add fail');
+ };
+ request.onsuccess = ({ result }) => {
+ resolve(result);
+ };
+ });
+};
+
+// Get file data according to fileName
+export const getImageFromCache = async fileName => {
+ const tx = dbInstance.transaction(['images']);
+ const request = tx.objectStore('images').index('fileName').get(fileName);
+ return new Promise((resolve, reject) => {
+ request.onerror = () => {
+ reject('data add fail');
+ };
+ request.onsuccess = ({ target }) => {
+ resolve(target.result);
+ };
+ });
+};
+```
+
+## save data
+
+When saving data, we can save the cache in the `transformData` of the method, because `transformData` will only be triggered when the network request responds, but will not be triggered when the cache is hit. In the sample code, convert the image blob instance to base64 data, cache and return this base64 data.
+
+```javascript-api.js
+import { addImage2Cache } from './db';
+
+export const image = fileName =>
+ alovaInst.Get(`/image/${fileName}`, {
+ // highlight-start
+ async transformData(imgBlob) {
+ // Asynchronously convert the blob to base64
+ const reader = new FileReader();
+ reader.readAsDataURL(imgBlob);
+ const base64Img = await new Promise(resolve => {
+ reader.onload = ({ target }) => {
+ resolve(target.result);
+ };
+ });
+
+ // Cache image data to IndexedDB
+ await addImage2Cache(fileName, base64Img);
+ return base64Img;
+ }
+ // highlight-end
+ });
+```
+
+## retrieve data
+
+Specify `localCache` of this method instance as an asynchronous function to change the cache into a controlled state, match the cache in IndexedDB in this function, and return it if it matches, otherwise return `undefined` and continue to initiate a request to obtain data.
+
+```javascript title=api.js
+import { getImageFromCache } from './db';
+
+export const image = fileName =>
+ alovaInst.Get(`/image/${fileName}`, {
+ async transformData(imgBlob) {
+ //...
+ },
+
+ // highlight-start
+ async localCache() {
+ // get cache
+ const cache = await getImageFromCache(fileName);
+ return cache && cache.data;
+ }
+ // highlight-end
+ });
+```
+
+In this way, a basic custom cache management is basically completed. You can also save the expiration time of the cache, and judge whether it has expired when the cache is matched in `localCache`, so as to realize the cache expiration function.
+
+IndexedDB is just one example of managing caches asynchronously, you can also connect to your cache servers to manage them.
diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md b/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md
new file mode 100644
index 000000000..da125fc16
--- /dev/null
+++ b/versioned_docs/version-2.x/tutorial/07-best-practice/04-multiple-servers.md
@@ -0,0 +1,19 @@
+---
+title: Multiple servers
+---
+
+If your project needs to request multiple servers, you can create multiple alova instances to correspond to different servers. In order to easily distinguish between different environments, you can also use environment variables to manage the hosts of multiple servers.
+
+```ts
+import { createAlova } from '@alova/core';
+
+//Create user-related alova instance
+const userAlova = createAlova({
+ baseURL: VITE_API_USER
+});
+
+//Create order-related alova instances
+const alova2 = createAlova({
+ baseURL: VITE_API_ORDER
+});
+```
diff --git a/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md b/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md
new file mode 100644
index 000000000..67afb2eb3
--- /dev/null
+++ b/versioned_docs/version-2.x/tutorial/07-best-practice/05-middleware.md
@@ -0,0 +1,28 @@
+---
+title: Common middleware practices
+---
+
+## Delay update loading
+
+When the response is very fast, the loading status will flash once, which will bring a bad experience to the user. Delaying the loading update can make the loading status display after a period of time. If the response is completed within this period, it will not appear. Loading status. Let's implement a middleware with delayed update loading.
+
+```javascript
+const delayLoadingMiddleware =
+ (delayTimer = 1000) =>
+ async (ctx, next) => {
+ //Control loading by yourself
+ ctx.controlLoading();
+
+ //Delay updates for a specific time
+ const timer = setTimeout(() => {
+ ctx.update({ loading: true });
+ }, delayTimer);
+ await next();
+ ctx.update({ loading: false });
+ clearTimeout(timer);
+ };
+
+useRequest(methodInstance, {
+ middleware: delayLoadingMiddleware()
+});
+```
diff --git a/docs/tutorial/07-best-practice/_category_.json b/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json
similarity index 92%
rename from docs/tutorial/07-best-practice/_category_.json
rename to versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json
index fe9037f89..d36db35d3 100644
--- a/docs/tutorial/07-best-practice/_category_.json
+++ b/versioned_docs/version-2.x/tutorial/07-best-practice/_category_.json
@@ -1,6 +1,6 @@
-{
- "label": "Best practice",
- "link": {
- "type": "generated-index"
- }
-}
+{
+ "label": "Best practice",
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/tutorial/08-request-adapter/01-alova-mock.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md
similarity index 88%
rename from docs/tutorial/08-request-adapter/01-alova-mock.md
rename to versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md
index e668d17c3..395ac3c78 100644
--- a/docs/tutorial/08-request-adapter/01-alova-mock.md
+++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/01-alova-mock.md
@@ -1,331 +1,330 @@
----
-title: Mock data
-sidebar_position: 10
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the scope of use of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off.
-
-## Features
-
-- ✨Works seamlessly with alova
-- ✨Arbitrary grouping of simulation requests to control global, group, and individual simulation interface enable and disable
-- ✨Works with mockjs
-- ✨Do not pollute the production environment
-
-## Install
-
-
-
-
-```bash
-npm install @alova/mock --save
-```
-
-
-
-
-```bash
-yarn add @alova/mock
-```
-
-
-
-
-The following is the usage flow.
-
-## Usage
-
-### Define the mock interface
-
-Use `defineMock` to define a set of mock interfaces. You can directly specify the return response data in each mock interface, or specify the response data to be dynamically calculated for the callback function.
-
-```javascript title=mockGrou1.js
-import { defineMock } from '@alova/mock';
-
-export default defineMock(
- {
- // capture get request
- '/todo': [1, 2, 3, 4],
-
- // rest style request
- '/todo/{id}': ({ params }) => {
- const id = params.id;
- // ...
- return {
- title: '...',
- time: '10:00'
- };
- },
-
- // capture post request
- '[POST]/todo': ({ query, data }) => {
- // ...
- return { success: true };
- },
-
- // return more detailed information
- '[POST]/todo': ({ query, data }) => {
- //...
- return {
- status: 403,
- statusText: 'unknown error',
- responseHeaders: {
- //...
- },
- body: {
- success: true
- }
- };
- },
-
- // simulate network error
- '[POST]/todo': ({ query, data }) => {
- throw new Error('network error');
- },
-
- // Add `-` before the key to disable this mock interface
- '-[DELETE]/todo/{id}': ({ params }) => {
- // ...
- return { success: true };
- }
- },
- true
-); // The second parameter indicates whether to enable this group of mock interfaces, the default is true, and can be specified as false to close
-```
-
-### Create mock request adapter
-
-Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete.
-
-```javascript
-import GlobalFetch from 'alova/GlobalFetch';
-import { createAlovaMockAdapter } from '@alova/mock';
-import mockGroup1 from './mockGroup1';
-
-// highlight-start
-const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], {
- // Global control whether the mock interface is enabled, the default is true
- enable: true,
-
- // Non-mock request adapter, used to send requests when the mock interface is not matched
- httpAdapter: GlobalFetch(),
-
- // mock interface response delay, in milliseconds
- delay: 1000,
-
- // Whether to print mock interface request information
- mockRequestLogger: true,
-
- // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova
- // The following is the default callback function, which is suitable for requesting the adapter using GlobalFetch
- // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback
- onMockResponse: data => new Response(JSON.stringify(data))
-});
-// highlight-end
-
-export const alovaInst = createAlova({
- baseURL: 'http://xxx',
-
- // Use the mock request adapter, if you need to switch adapters, please see the following practical suggestions
- requestAdapter: mockAdapter,
-
- statesHook: /** ... */
-});
-```
-
-### Paths match mode
-
-:::info version required
-
-1.5.0+
-
-:::
-
-By default, the path defined in `defineMock` is the full pathname of a url, see the following code snippet.
-
-```javascript
-const alovaInst = createAlova({
- baseURL: 'https://api.alovajs.org'
- //...
-});
-alovaInst.Get('/user?id=1').send();
-```
-
-When the request path in the example is `https://api.alovajs.org/user?id=1`, its full pathname is `/user`, which can match `/user` in `defineMock`.
-
-Usually this is enough, but when your baseURL is not just a domain name.
-
-```javascript
-const alovaInst = createAlova({
- baseURL: 'https://api.alovajs.org/v1/subname'
- //...
-});
-alovaInst.Get('/user?id=1').send();
-```
-
-In this example, the request path is `https://api.alovajs.org/v1/subname/user?id=1`, the matching path of the mock is `/v1/subname/user`, and `/ in the baseURL needs to be v1/subname` is also written together, which is slightly redundant when the number of interfaces is large.
-
-At this point, you can set `matchMode` to `methodurl` in `createAlovaMockAdapter`, it will only match the url defined in the method instance, for example, the above instance will match `/user?id=1` instead of The part in baseURL needs to be written. On the contrary, if the url in the method instance has a get parameter, it also needs to be written in the matching path of `defineMock`, just like `?id=1` here.
-
-```javascript
-createAlovaMockAdapter([mockGroup1 /** ... */], {
- //...
- // highlight-start
- matchMode: 'methodurl'
- // highlight-end
-});
-```
-
-## Practical advice
-
-### Group interfaces per developer per version
-
-In the team development scenario, we often only need to simulate some undeveloped interfaces for each version development, and use the test environment interface for the interface of the previous version. At this time, in order to achieve better simulation interface management, you can use The two dimensions, development version and developer, group interfaces.
-
-For example, there are two developers named _August_, _kevin_, they are developing v1.1 product features, they can manage the mock interface like this.
-
-```javascript title=August-v1.1.js
-import { defineMock } from '@alova/mock';
-
-export default defineMock({
- '/todo': [
- /** */
- ],
- '[POST]/todo': ({ data }) => {
- // ...
- // return...
- }
- // ...
-});
-```
-
-```javascript title=kevin-v1.1.js
-import { defineMock } from '@alova/mock';
-
-export default defineMock({
- '[PUT]/todo/add': ({ data }) => {
- // ...
- // return...
- },
- '[DELETE]/todo/remove': ({ data }) => {
- // ...
- // return...
- }
- // ...
-});
-```
-
-```javascript title=request.js
-import Augustv1_1 from './August-v1.1';
-import Keevenv1_1 from './kevin-v1.1';
-
-const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], {
- httpAdapter: GlobalFetch(),
- delay: 1000
-});
-export const alovaInst = createAlova({
- baseURL: 'http://xxx',
- requestAdapter: mockAdapter
- // ...
-});
-```
-
-### Exclude mock code in production
-
-The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do:
-
-```javascript
-const globalFetch = GlobalFetch();
-const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], {
- httpAdapter: globalFetch,
- delay: 1000,
-});
-
-export const alovaInst = createAlova({
- baseURL: 'http://xxx',
-
- // highlight-start
- // In the production environment controlled by environment variables, the mock-related code will not be packaged in
- requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch,
- // highlight-end
-
- statesHook: /** ... */
-});
-```
-
-### Use with mockjs
-
-If you don't want to write the mock data yourself, but use it with a mock js library (such as mockjs), you can do so.
-
-```javascript
-import { defineMock } from '@alova/mock';
-import Mock from 'mockjs';
-
-export default defineMock({
- '/api1': Mock.mock({
- 'id|1-10000': 100
- })
-});
-```
-
-## Convert mock data
-
-**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `GlobalFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format.
-
-### Convert response data
-
-You can intercept the mock response data in the `onMockResponse` field and return the transformed response data and response headers.
-
-> You can also throw an ERROR in onMockResponse to indicate a failure request.
-
-```javascript
-const mockAdapter = createAlovaMockAdapter(
- [
- /* mock data */
- ],
- {
- //...
- // highlight-start
- onMockResponse(response, request, currentMethod) {
- // response is the corresponding data set, which contains status, statusText, responseHeaders, body
- // request is the request data, which contains query, params, headers, data
- // currentMethod is the method instance of the current request
- //...
- // Return converted response data and response headers
- return {
- response: /** response data */,
- headers: /** Response headers */
- };
- }
- // highlight-end
- }
-);
-```
-
-### Convert Error Instance
-
-You can intercept the error instance in the `onMockError` field and return the converted error message.
-
-> You can also throw an ERROR in onMockResponse to indicate failure request.
-
-```javascript
-const mockAdapter = createAlovaMockAdapter(
- [
- /* mock data */
- ],
- {
- //...
- // highlight-start
- onMockError(error, currentMethod) {
- // error is an error instance
- // currentMethod is the method instance of the current request
- //...
- // Return the converted error message collection
- }
- // highlight-end
- }
-);
-```
+---
+title: Mock data
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+This mock plug-in is an alova request adapter. Different from the traditional Proxy form, you can control the Scope of usage of mock data. You can control the global scope, a group of interface scopes, and even the enabling and use of a certain interface. Disabled, which is very useful in our actual business scenarios. Each iteration will add or modify a set of interfaces. We hope that the previous functions will still follow the developed interfaces, and let the new or modified interfaces Taking the simulation data, at this time, each developer can group the interfaces involved in this iteration into a group, and turn them on or off.
+
+## Features
+
+- Works seamlessly with alova
+- Arbitrary grouping of simulation requests to control global, group, and individual simulation interface enable and disable
+- Works with mockjs
+- Do not pollute the production environment
+
+## Install
+
+
+
+
+```bash
+npm install @alova/mock --save
+```
+
+
+
+
+```bash
+yarn add @alova/mock
+```
+
+
+
+
+The following is the usage flow.
+
+## Usage
+
+### Define the mock interface
+
+Use `defineMock` to define a set of mock interfaces. You can directly specify the return response data in each mock interface, or specify the response data to be dynamically calculated for the callback function.
+
+```javascript title=mockGrou1.js
+import { defineMock } from '@alova/mock';
+
+export default defineMock(
+ {
+ // capture get request
+ '/todo': [1, 2, 3, 4],
+
+ // rest style request
+ '/todo/{id}': ({ params }) => {
+ const id = params.id;
+ // ...
+ return {
+ title: '...',
+ time: '10:00'
+ };
+ },
+
+ // capture post request
+ '[POST]/todo': ({ query, data }) => {
+ // ...
+ return { success: true };
+ },
+
+ // return more detailed information
+ '[POST]/todo': ({ query, data }) => {
+ //...
+ return {
+ status: 403,
+ statusText: 'unknown error',
+ responseHeaders: {
+ //...
+ },
+ body: {
+ success: true
+ }
+ };
+ },
+
+ // simulate network error
+ '[POST]/todo': ({ query, data }) => {
+ throw new Error('network error');
+ },
+
+ // Add `-` before the key to disable this mock interface
+ '-[DELETE]/todo/{id}': ({ params }) => {
+ // ...
+ return { success: true };
+ }
+ },
+ true
+); // The second parameter indicates whether to enable this group of mock interfaces, the default is true, and can be specified as false to close
+```
+
+### Create mock request adapter
+
+Create a mock request adapter when calling `createAlova`, and pass in the mock interface to complete.
+
+```javascript
+import GlobalFetch from 'alova/GlobalFetch';
+import { createAlovaMockAdapter } from '@alova/mock';
+import mockGroup1 from './mockGroup1';
+
+// highlight-start
+const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], {
+ // Global control whether the mock interface is enabled, the default is true
+ enable: true,
+
+ // Non-mock request adapter, used to send requests when the mock interface is not matched
+ httpAdapter: GlobalFetch(),
+
+ // mock interface response delay, in milliseconds
+ delay: 1000,
+
+ // Whether to print mock interface request information
+ mockRequestLogger: true,
+
+ // Simulation interface callback, data is the returned simulation data, you can use it to construct any object you want and return it to alova
+ // The following is the default callback function, which is suitable for requesting the adapter using GlobalFetch
+ // If you are using other request adapters, please customize the return data structure suitable for the adapter in the mock interface callback
+ onMockResponse: data => new Response(JSON.stringify(data))
+});
+// highlight-end
+
+export const alovaInst = createAlova({
+ baseURL: 'http://xxx',
+
+ // Use the mock request adapter, if you need to switch adapters, please see the following practical suggestions
+ requestAdapter: mockAdapter,
+
+ statesHook: /** ... */
+});
+```
+
+### Paths match mode
+
+:::info version required
+
+1.5.0+
+
+:::
+
+By default, the path defined in `defineMock` is the full pathname of a url, see the following code snippet.
+
+```javascript
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org'
+ //...
+});
+alovaInst.Get('/user?id=1').send();
+```
+
+When the request path in the example is `https://api.alovajs.org/user?id=1`, its full pathname is `/user`, which can match `/user` in `defineMock`.
+
+Usually this is enough, but when your baseURL is not just a domain name.
+
+```javascript
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org/v1/subname'
+ //...
+});
+alovaInst.Get('/user?id=1').send();
+```
+
+In this example, the request path is `https://api.alovajs.org/v1/subname/user?id=1`, the matching path of the mock is `/v1/subname/user`, and `/ in the baseURL needs to be v1/subname` is also written together, which is slightly redundant when the number of interfaces is large.
+
+At this point, you can set `matchMode` to `methodurl` in `createAlovaMockAdapter`, it will only match the url defined in the method instance, for example, the above instance will match `/user?id=1` instead of The part in baseURL needs to be written. On the contrary, if the url in the method instance has a get parameter, it also needs to be written in the matching path of `defineMock`, just like `?id=1` here.
+
+```javascript
+createAlovaMockAdapter([mockGroup1 /** ... */], {
+ //...
+ // highlight-start
+ matchMode: 'methodurl'
+ // highlight-end
+});
+```
+
+## Practical advice
+
+### Group interfaces per developer per version
+
+In the team development scenario, we often only need to simulate some undeveloped interfaces for each version development, and use the test environment interface for the interface of the previous version. At this time, in order to achieve better simulation interface management, you can use The two dimensions, development version and developer, group interfaces.
+
+For example, there are two developers named _August_, _kevin_, they are developing v1.1 product features, they can manage the mock interface like this.
+
+```javascript title=August-v1.1.js
+import { defineMock } from '@alova/mock';
+
+export default defineMock({
+ '/todo': [
+ /** */
+ ],
+ '[POST]/todo': ({ data }) => {
+ // ...
+ // return...
+ }
+ // ...
+});
+```
+
+```javascript title=kevin-v1.1.js
+import { defineMock } from '@alova/mock';
+
+export default defineMock({
+ '[PUT]/todo/add': ({ data }) => {
+ // ...
+ // return...
+ },
+ '[DELETE]/todo/remove': ({ data }) => {
+ // ...
+ // return...
+ }
+ // ...
+});
+```
+
+```javascript title=request.js
+import Augustv1_1 from './August-v1.1';
+import Keevenv1_1 from './kevin-v1.1';
+
+const mockAdapter = createAlovaMockAdapter([Augustv1_1, kevinv1_1], {
+ httpAdapter: GlobalFetch(),
+ delay: 1000
+});
+export const alovaInst = createAlova({
+ baseURL: 'http://xxx',
+ requestAdapter: mockAdapter
+ // ...
+});
+```
+
+### Exclude mock code in production
+
+The mock data is generally only used in the development environment, and will be switched to the actual interface in the production environment, so this mock code becomes useless in the production environment. At this time, we can exclude this code by judging the environment variables. , you just need to do:
+
+```javascript
+const globalFetch = GlobalFetch();
+const mockAdapter = createAlovaMockAdapter([mockGroup1, /** ... */], {
+ httpAdapter: globalFetch,
+ delay: 1000,
+});
+
+export const alovaInst = createAlova({
+ baseURL: 'http://xxx',
+
+ // highlight-start
+ // In the production environment controlled by environment variables, the mock-related code will not be packaged in
+ requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : globalFetch,
+ // highlight-end
+
+ statesHook: /** ... */
+});
+```
+
+### Use with mockjs
+
+If you don't want to write the mock data yourself, but use it with a mock js library (such as mockjs), you can do so.
+
+```javascript
+import { defineMock } from '@alova/mock';
+import Mock from 'mockjs';
+
+export default defineMock({
+ '/api1': Mock.mock({
+ 'id|1-10000': 100
+ })
+});
+```
+
+## Convert mock data
+
+**@alova/mock** By default, the response data is packaged as a Response instance, and the response header is packaged as a Headers instance by default, which is adapted for `GlobalFetch`, but if you use other request adapters, you need to mock the data Convert to the corresponding format.
+
+### Convert response data
+
+You can intercept the mock response data in the `onMockResponse` field and return the transformed response data and response headers.
+
+> You can also throw an ERROR in onMockResponse to indicate a failure request.
+
+```javascript
+const mockAdapter = createAlovaMockAdapter(
+ [
+ /* mock data */
+ ],
+ {
+ //...
+ // highlight-start
+ onMockResponse(response, request, currentMethod) {
+ // response is the corresponding data set, which contains status, statusText, responseHeaders, body
+ // request is the request data, which contains query, params, headers, data
+ // currentMethod is the method instance of the current request
+ //...
+ // Return converted response data and response headers
+ return {
+ response: /** response data */,
+ headers: /** Response headers */
+ };
+ }
+ // highlight-end
+ }
+);
+```
+
+### Convert Error Instance
+
+You can intercept the error instance in the `onMockError` field and return the converted error message.
+
+> You can also throw an ERROR in onMockResponse to indicate failure request.
+
+```javascript
+const mockAdapter = createAlovaMockAdapter(
+ [
+ /* mock data */
+ ],
+ {
+ //...
+ // highlight-start
+ onMockError(error, currentMethod) {
+ // error is an error instance
+ // currentMethod is the method instance of the current request
+ //...
+ // Return the converted error message collection
+ }
+ // highlight-end
+ }
+);
+```
diff --git a/docs/tutorial/08-request-adapter/02-alova-adapter-xhr.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md
similarity index 96%
rename from docs/tutorial/08-request-adapter/02-alova-adapter-xhr.md
rename to versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md
index 4959a1b1f..930f63124 100644
--- a/docs/tutorial/08-request-adapter/02-alova-adapter-xhr.md
+++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/02-alova-adapter-xhr.md
@@ -1,337 +1,336 @@
----
-title: XMLHttpRequest Adapter
-sidebar_position: 20
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-## Install
-
-
-
-
-```bash
-npm install @alova/adapter-xhr --save
-```
-
-
-
-
-```bash
-yarn add @alova/adapter-xhr
-```
-
-
-
-
-## Instructions
-
-### create alova
-
-Use **xhrRequestAdapter** as request adapter for alova.
-
-```javascript
-import { createAlova } from 'alova';
-import { xhrRequestAdapter } from '@alova/adapter-xhr';
-
-const alovaInst = createAlova({
- //...
- requestAdapter: xhrResponseAdapter()
- //...
-});
-```
-
-### Request
-
-The XMLHttpRequest adapter provides basic configuration parameters, including `responseType`, `withCredentials`, `mimeType`, `auth`, as follows:
-
-
-
-
-```html
-
-
Loading...
-
The request data is: {{ data }}
-
-
-
-```
-
-
-
-
-```jsx
-const list = () =>
- alovaInst.Get('/list', {
- /**
- * Set the response data type
- * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json" and "text"
- * defaults to "json"
- */
- responseType: 'text',
-
- /**
- * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. Default is false
- */
- withCredentials: true,
-
- /**
- * Set the mimeType of the response data
- */
- mimeType: 'text/plain; charset=x-user-defined',
-
- /**
- * auth means use HTTP Basic authentication and provide credentials.
- * This will set an `Authorization` header, overriding any existing
- * Custom headers for `Authorization` set using `headers`.
- * Note that only HTTP Basic authentication can be configured via this parameter.
- * For Bearer tokens etc., use the `Authorization` custom header instead.
- */
- auth: {
- username: 'name1',
- password: '123456'
- }
- });
-
-const App = () => {
- const { loading, data } = useRequest(list);
-
- return (
- { loading ?
-```
-
-
-
-
-### Upload
-
-Use `FormData` to upload files, and this `FormData` instance will be sent to the server through `xhr.send`. It will be set `Content-Type` automatically, you don't need to custom it with `multipart/form-data`.
-
-```javascript
-const uploadFile = imageFile => {
- const formData = new FormData();
- formData.append('file', imageFile);
- return alovaInst.Post('/uploadImg', formData, {
- // Start upload progress
- enableUpload: true
- });
-};
-
-const {
- loading,
- data,
- uploading,
- send: sendUpload
-} = useRequest(uploadFile, {
- immediate: false
-});
-
-// Picture selection event callback
-const handleImageChoose = ({ target }) => {
- sendUpload(target.files[0]);
-};
-```
-
-### download
-
-Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true.
-
-```javascript
-const downloadFile = () =>
- alovaInst.Get('/bigImage.jpg', {
- // Start download progress
- enableDownload: true,
- responseType: 'blob'
- });
-
-const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, {
- immediate: false
-});
-onSuccess(({ data: imageBlob }) => {
- // download image
- const anchor = document.createElement('a');
- anchor.href = URL.createObjectURL(blob);
- anchor.download = 'image.jpg';
- anchor.click();
- URL.revokeObjectURL(anchor.href);
-});
-const handleImageDownload = () => {
- send();
-};
-```
-
-## Mock request adapter compatible
-
-When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the XMLHttpRequest adapter, we You need to adapt the response data of the mock request adapter to the XMLHttpRequest adapter. In this case, you need to use the `xhrMockResponse` exported in the **@alova/adapter-xhr** package as the response adapter.
-
-```javascript
-import { defineMock, createAlovaMockAdapter } from '@alova/mock';
-import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr';
-
-const mocks = defineMock({
- //...
-});
-
-// mock data request adapter
-export default createAlovaMockAdapter([mocks], {
- // After specifying the request adapter, requests that do not match the simulated interface will use this adapter to send requests
- httpAdapter: xhrRequestAdapter(),
-
- // Use xhrMockResponse to adapt the simulated data to the XMLHttpRequest adapter
- onMockResponse: xhrMockResponse
-});
-
-export const alovaInst = createAlova({
- //...
- // Control whether to use the simulated request adapter through environment variables
- requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter()
-});
-```
-
-## Typescript
-
-The XMLHttpRequest request adapter provides complete type adaptation.
-
-### method configuration
-
-When creating a method instance, in addition to the common configuration items in the method, you can also use the configuration items in `AlovaXHRRequestConfig`.
-
-```typescript
-/**
- * xhr request configuration parameters
- */
-interface AlovaXHRRequestConfig {
- /**
- * Set the response data type.
- *
- * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json", and "text".
- * Setting 1: If the current global object is not a Window object, the setting to "document" is ignored.
- * Setup 2: Throw an "InvalidStateError" DOMException if the state is loading or complete.
- * Setting 3: Throws an "InvalidAccessError" DOMException if the sync flag is set and the current global object is a Window object.
- * @default "json"
- */
- responseType?: XMLHttpRequestResponseType;
-
- /**
- * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. The default is false.
- * An 'InvalidStateError' DOMException is thrown if the state is not sent or not opened, or if the send() flag is set.
- * @default false
- */
- withCredentials?: boolean;
-
- /**
- * Set the mimeType of the response data
- */
- mimeType?: string;
-
- /**
- * `auth` indicates that HTTP Basic authentication should be used, and credentials are provided.
- * This will set an `Authorization` header, overriding any existing
- * Custom headers for `Authorization` set using `headers`.
- * Note that only HTTP Basic authentication can be configured via this parameter.
- * For Bearer tokens etc., use the `Authorization` custom header instead.
- */
- auth?: {
- username: string;
- password: string;
- };
-}
-```
-
-### Response data
-
-XMLHttpRequest adapter response data is as follows:
-
-```typescript
-interface AlovaXHRResponseHeaders {
- [x: string]: any;
-}
-interface AlovaXHRResponse {
- status: number;
- statusText: string;
- data: T;
- headers: AlovaXHRResponseHeaders;
-}
-```
+---
+title: XMLHttpRequest Adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Install
+
+
+
+
+```bash
+npm install @alova/adapter-xhr --save
+```
+
+
+
+
+```bash
+yarn add @alova/adapter-xhr
+```
+
+
+
+
+## Instructions
+
+### create alova
+
+Use **xhrRequestAdapter** as request adapter for alova.
+
+```javascript
+import { createAlova } from 'alova';
+import { xhrRequestAdapter } from '@alova/adapter-xhr';
+
+const alovaInst = createAlova({
+ //...
+ requestAdapter: xhrResponseAdapter()
+ //...
+});
+```
+
+### Request
+
+The XMLHttpRequest adapter provides basic configuration parameters, including `responseType`, `withCredentials`, `mimeType`, `auth`, as follows:
+
+
+
+
+```html
+
+
Loading...
+
The request data is: {{ data }}
+
+
+
+```
+
+
+
+
+```jsx
+const list = () =>
+ alovaInst.Get('/list', {
+ /**
+ * Set the response data type
+ * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json" and "text"
+ * defaults to "json"
+ */
+ responseType: 'text',
+
+ /**
+ * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. Default is false
+ */
+ withCredentials: true,
+
+ /**
+ * Set the mimeType of the response data
+ */
+ mimeType: 'text/plain; charset=x-user-defined',
+
+ /**
+ * auth means use HTTP Basic authentication and provide credentials.
+ * This will set an `Authorization` header, overriding any existing
+ * Custom headers for `Authorization` set using `headers`.
+ * Note that only HTTP Basic authentication can be configured via this parameter.
+ * For Bearer tokens etc., use the `Authorization` custom header instead.
+ */
+ auth: {
+ username: 'name1',
+ password: '123456'
+ }
+ });
+
+const App = () => {
+ const { loading, data } = useRequest(list);
+
+ return (
+ { loading ?
+```
+
+
+
+
+### Upload
+
+Use `FormData` to upload files, and this `FormData` instance will be sent to the server through `xhr.send`. It will be set `Content-Type` automatically, you don't need to custom it with `multipart/form-data`.
+
+```javascript
+const uploadFile = imageFile => {
+ const formData = new FormData();
+ formData.append('file', imageFile);
+ return alovaInst.Post('/uploadImg', formData, {
+ // Start upload progress
+ enableUpload: true
+ });
+};
+
+const {
+ loading,
+ data,
+ uploading,
+ send: sendUpload
+} = useRequest(uploadFile, {
+ immediate: false
+});
+
+// Picture selection event callback
+const handleImageChoose = ({ target }) => {
+ sendUpload(target.files[0]);
+};
+```
+
+### download
+
+Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true.
+
+```javascript
+const downloadFile = () =>
+ alovaInst.Get('/bigImage.jpg', {
+ // Start download progress
+ enableDownload: true,
+ responseType: 'blob'
+ });
+
+const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, {
+ immediate: false
+});
+onSuccess(({ data: imageBlob }) => {
+ // download image
+ const anchor = document.createElement('a');
+ anchor.href = URL.createObjectURL(blob);
+ anchor.download = 'image.jpg';
+ anchor.click();
+ URL.revokeObjectURL(anchor.href);
+});
+const handleImageDownload = () => {
+ send();
+};
+```
+
+## Mock request adapter compatible
+
+When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the XMLHttpRequest adapter, we You need to adapt the response data of the mock request adapter to the XMLHttpRequest adapter. In this case, you need to use the `xhrMockResponse` exported in the **@alova/adapter-xhr** package as the response adapter.
+
+```javascript
+import { defineMock, createAlovaMockAdapter } from '@alova/mock';
+import { xhrRequestAdapter, xhrMockResponse } from '@alova/adapter-xhr';
+
+const mocks = defineMock({
+ //...
+});
+
+// mock data request adapter
+export default createAlovaMockAdapter([mocks], {
+ // After specifying the request adapter, requests that do not match the simulated interface will use this adapter to send requests
+ httpAdapter: xhrRequestAdapter(),
+
+ // Use xhrMockResponse to adapt the simulated data to the XMLHttpRequest adapter
+ onMockResponse: xhrMockResponse
+});
+
+export const alovaInst = createAlova({
+ //...
+ // Control whether to use the simulated request adapter through environment variables
+ requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : xhrRequestAdapter()
+});
+```
+
+## Typescript
+
+The XMLHttpRequest request adapter provides complete type adaptation.
+
+### method configuration
+
+When creating a method instance, in addition to the common configuration items in the method, you can also use the configuration items in `AlovaXHRRequestConfig`.
+
+```typescript
+/**
+ * xhr request configuration parameters
+ */
+interface AlovaXHRRequestConfig {
+ /**
+ * Set the response data type.
+ *
+ * Can be set to change the response type. Values are: "arraybuffer", "blob", "document", "json", and "text".
+ * Setting 1: If the current global object is not a Window object, the setting to "document" is ignored.
+ * Setup 2: Throw an "InvalidStateError" DOMException if the state is loading or complete.
+ * Setting 3: Throws an "InvalidAccessError" DOMException if the sync flag is set and the current global object is a Window object.
+ * @default "json"
+ */
+ responseType?: XMLHttpRequestResponseType;
+
+ /**
+ * True when credentials are to be included in cross-origin requests. false when they are excluded from cross-origin requests and when cookies are ignored in their responses. The default is false.
+ * An 'InvalidStateError' DOMException is thrown if the state is not sent or not opened, or if the send() flag is set.
+ * @default false
+ */
+ withCredentials?: boolean;
+
+ /**
+ * Set the mimeType of the response data
+ */
+ mimeType?: string;
+
+ /**
+ * `auth` indicates that HTTP Basic authentication should be used, and credentials are provided.
+ * This will set an `Authorization` header, overriding any existing
+ * Custom headers for `Authorization` set using `headers`.
+ * Note that only HTTP Basic authentication can be configured via this parameter.
+ * For Bearer tokens etc., use the `Authorization` custom header instead.
+ */
+ auth?: {
+ username: string;
+ password: string;
+ };
+}
+```
+
+### Response data
+
+XMLHttpRequest adapter response data is as follows:
+
+```typescript
+interface AlovaXHRResponseHeaders {
+ [x: string]: any;
+}
+interface AlovaXHRResponse {
+ status: number;
+ statusText: string;
+ data: T;
+ headers: AlovaXHRResponseHeaders;
+}
+```
diff --git a/docs/tutorial/08-request-adapter/03-alova-adapter-axios.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md
similarity index 96%
rename from docs/tutorial/08-request-adapter/03-alova-adapter-axios.md
rename to versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md
index 93b936a8f..63cd84234 100644
--- a/docs/tutorial/08-request-adapter/03-alova-adapter-axios.md
+++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/03-alova-adapter-axios.md
@@ -1,293 +1,292 @@
----
-title: axios adapter
-sidebar_position: 30
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-## Install
-
-
-
-
-```bash
-npm install @alova/adapter-axios --save
-```
-
-
-
-
-```bash
-yarn add @alova/adapter-axios
-```
-
-
-
-
-## Instructions
-
-### create alova
-
-Use **axiosRequestAdapter** as request adapter for alova.
-
-```javascript
-import { createAlova } from 'alova';
-import { axiosRequestAdapter } from '@alova/adapter-axios';
-
-const alovaInst = createAlova({
- //...
- requestAdapter: axiosRequestAdapter()
- //...
-});
-```
-
-The adapter will use the default axios instance to make requests internally. If you set some global parameters for axios, you may need to pay attention to the following two points:
-
-1. Priority is given to using the `baseURL` and `timeout` parameters in the axios instance, so if you set these parameters on the axios instance, you do not need to set them in `createAlova`;
-2. The `beforeRequest` hook of the alova instance will be triggered earlier than the `interceptor.request` of the axios instance, and the `responded` hook of the alova will be triggered later than the `interceptor.response` of the axios instance;
-
-> You can also [use custom axios instance](#use-custom-axios-instance)
-
-## usage
-
-### request
-
-The usage of request is exactly the same as that used in the web environment. Already fully compatible with **axios**, you can specify [all configuration items](https://axios-http.com/docs/req_config) supported by `axios` in _config_ of method instance creation.
-
-
-
-
-```html
-
-
Loading...
-
The request data is: {{ data }}
-
-
-
-```
-
-
-
-
-```jsx
-const list = () =>
- alovaInst.Get('/list', {
- // The set parameters will be passed to axios
- paramsSerializer: params => {
- return Qs.stringify(params, {arrayFormat: 'brackets'})
- },
- });
-
-const App = () => {
- const { loading, data } = useRequest(list);
-
- return (
- { loading ?
-```
-
-
-
-
-### Upload
-
-Use `FormData` to upload files, and this `FormData` instance will be passed to axios, which is consistent with the usage of axios upload files.
-
-```javascript
-const uploadFile = imageFile => {
- const formData = new FormData();
- formData.append('file', imageFile);
- return alovaInst.Post('/uploadImg', formData, {
- // Start upload progress
- enableUpload: true
- });
-};
-
-const {
- loading,
- data,
- uploading,
- send: sendUpload
-} = useRequest(uploadFile, {
- immediate: false
-});
-
-// Picture selection event callback
-const handleImageChoose = ({ target }) => {
- sendUpload(target.files[0]);
-};
-```
-
-### download
-
-Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true.
-
-```javascript
-const downloadFile = () =>
- alovaInst.Get('/bigImage.jpg', {
- // Start download progress
- enableDownload: true,
- responseType: 'blob'
- });
-
-const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, {
- immediate: false
-});
-onSuccess(({ data: imageBlob }) => {
- // download image
- const anchor = document.createElement('a');
- anchor.href = URL.createObjectURL(blob);
- anchor.download = 'image.jpg';
- anchor.click();
- URL.revokeObjectURL(anchor.href);
-});
-const handleImageDownload = () => {
- send();
-};
-```
-
-## Use custom axios instance
-
-By default, this adapter will use the default axios instance for requests, but in some cases you need to use a custom created axios instance. You can do this:
-
-```javascript
-const customAxios = axios.create({
- // ...
-});
-
-const alovaInst = createAlova({
- // ...
- // highlight-start
- requestAdapter: axiosRequestAdapter({
- axios: customAxios
- })
- // highlight-end
-});
-```
-
-## Mock request adapter compatible
-
-When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the axios adapter, we The response data of the mock request adapter needs to be compatible with **AxiosResponse**, and the error instance is **AxiosError**, so you need to use `axiosMockResponse` exported from the **@alova/adapter-axios** package as the response adapter .
-
-```javascript
-import { defineMock, createAlovaMockAdapter } from '@alova/mock';
-import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios';
-
-const mocks = defineMock({
- //...
-});
-
-// mock data request adapter
-export default createAlovaMockAdapter([mocks], {
- // After specifying axios request adapter, requests that do not match the simulated interface will use this adapter to send requests
- httpAdapter: axiosRequestAdapter(),
-
- // axiosMockResponse contains onMockResponse and onMockError
- // Used to convert mock data to AxiosResponse and AxiosError compatible format
- ...axiosMockResponse
-});
-
-export const alovaInst = createAlova({
- //...
- // Control whether to use the simulated request adapter through environment variables
- requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter()
-});
-```
-
-##Typescript
-
-The axios request adapter provides complete type adaptation. The type of method configuration and response data will exactly match the type of axios.
-
-### method configuration
-
-When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `AxiosRequestConfig`, we have removed the items that conflict with the general configuration of the method instance in the type.
-
-```typescript
-/**
- * axios request configuration parameters
- * Removed the attributes that conflicted with the method
- */
-export type AlovaAxiosRequestConfig = Omit<
- AxiosRequestConfig,
- | 'url'
- | 'method'
- | 'baseURL'
- | 'headers'
- | 'params'
- | 'data'
- | 'timeout'
- | 'cancelToken'
- | 'signal'
- | 'onUploadProgress'
- | 'onDownloadProgress'
->;
-```
-
-### Response data
-
-The response data type of axios is `AxiosResponse`, when you use the axios adapter, you will also get the response data in the same format. In actual use, we usually need to process response data globally. A simple example is as follows:
-
-```typescript
-const alovaInst = createAlova(
- baseURL: 'https://api.alovajs.org',
- requestAdapter: axiosRequestAdapter(),
- responded(response) {
- // response is automatically inferred as AxiosResponse type
- return response.data;
- }
-});
-```
-
-### Error
-
-When axios receives non-20x and 30x response status codes, it will throw an error. In order to contain more information, axios custom-designed the error instance into an `AxiosError` instance instead of a normal Error instance, so when encountering An error will be thrown when there is a server error or a network error, and you can catch it in the global error callback.
-
-```typescript
-const alovaInst = createAlova(
- baseURL: 'https://api.alovajs.org',
- requestAdapter: axiosRequestAdapter(),
- responded: {
- onSuccess(response) {
- // response is automatically inferred as AxiosResponse type
- return response.data;
- },
- onError(err: AxiosError) {
- // err type is any by default, you can cast it to AxiosError
- //...
- }
- }
-});
-```
+---
+title: axios adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Install
+
+
+
+
+```bash
+npm install @alova/adapter-axios --save
+```
+
+
+
+
+```bash
+yarn add @alova/adapter-axios
+```
+
+
+
+
+## Instructions
+
+### create alova
+
+Use **axiosRequestAdapter** as request adapter for alova.
+
+```javascript
+import { createAlova } from 'alova';
+import { axiosRequestAdapter } from '@alova/adapter-axios';
+
+const alovaInst = createAlova({
+ //...
+ requestAdapter: axiosRequestAdapter()
+ //...
+});
+```
+
+The adapter will use the default axios instance to make requests internally. If you set some global parameters for axios, you may need to pay attention to the following two points:
+
+1. Priority is given to using the `baseURL` and `timeout` parameters in the axios instance, so if you set these parameters on the axios instance, you do not need to set them in `createAlova`;
+2. The `beforeRequest` hook of the alova instance will be triggered earlier than the `interceptor.request` of the axios instance, and the `responded` hook of the alova will be triggered later than the `interceptor.response` of the axios instance;
+
+> You can also [use custom axios instance](#use-custom-axios-instance)
+
+## usage
+
+### request
+
+The usage of request is exactly the same as that used in the web environment. Already fully compatible with **axios**, you can specify [all configuration items](https://axios-http.com/docs/req_config) supported by `axios` in _config_ of method instance creation.
+
+
+
+
+```html
+
+
Loading...
+
The request data is: {{ data }}
+
+
+
+```
+
+
+
+
+```jsx
+const list = () =>
+ alovaInst.Get('/list', {
+ // The set parameters will be passed to axios
+ paramsSerializer: params => {
+ return Qs.stringify(params, {arrayFormat: 'brackets'})
+ },
+ });
+
+const App = () => {
+ const { loading, data } = useRequest(list);
+
+ return (
+ { loading ?
+```
+
+
+
+
+### Upload
+
+Use `FormData` to upload files, and this `FormData` instance will be passed to axios, which is consistent with the usage of axios upload files.
+
+```javascript
+const uploadFile = imageFile => {
+ const formData = new FormData();
+ formData.append('file', imageFile);
+ return alovaInst.Post('/uploadImg', formData, {
+ // Start upload progress
+ enableUpload: true
+ });
+};
+
+const {
+ loading,
+ data,
+ uploading,
+ send: sendUpload
+} = useRequest(uploadFile, {
+ immediate: false
+});
+
+// Picture selection event callback
+const handleImageChoose = ({ target }) => {
+ sendUpload(target.files[0]);
+};
+```
+
+### download
+
+Point the request url to the file address to download, you can also enable the download progress by setting `enableDownload` to true.
+
+```javascript
+const downloadFile = () =>
+ alovaInst.Get('/bigImage.jpg', {
+ // Start download progress
+ enableDownload: true,
+ responseType: 'blob'
+ });
+
+const { loading, data, downloading, send, onSuccess } = useRequest(downloadFile, {
+ immediate: false
+});
+onSuccess(({ data: imageBlob }) => {
+ // download image
+ const anchor = document.createElement('a');
+ anchor.href = URL.createObjectURL(blob);
+ anchor.download = 'image.jpg';
+ anchor.click();
+ URL.revokeObjectURL(anchor.href);
+});
+const handleImageDownload = () => {
+ send();
+};
+```
+
+## Use custom axios instance
+
+By default, this adapter will use the default axios instance for requests, but in some cases you need to use a custom created axios instance. You can do this:
+
+```javascript
+const customAxios = axios.create({
+ // ...
+});
+
+const alovaInst = createAlova({
+ // ...
+ // highlight-start
+ requestAdapter: axiosRequestAdapter({
+ axios: customAxios
+ })
+ // highlight-end
+});
+```
+
+## Mock request adapter compatible
+
+When developing applications, we may still need to use simulated requests. Only by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, which is compatible with the `GlobalFetch` request adapter by default. When using the axios adapter, we The response data of the mock request adapter needs to be compatible with **AxiosResponse**, and the error instance is **AxiosError**, so you need to use `axiosMockResponse` exported from the **@alova/adapter-axios** package as the response adapter .
+
+```javascript
+import { defineMock, createAlovaMockAdapter } from '@alova/mock';
+import { axiosRequestAdapter, axiosMockResponse } from '@alova/adapter-axios';
+
+const mocks = defineMock({
+ //...
+});
+
+// mock data request adapter
+export default createAlovaMockAdapter([mocks], {
+ // After specifying axios request adapter, requests that do not match the simulated interface will use this adapter to send requests
+ httpAdapter: axiosRequestAdapter(),
+
+ // axiosMockResponse contains onMockResponse and onMockError
+ // Used to convert mock data to AxiosResponse and AxiosError compatible format
+ ...axiosMockResponse
+});
+
+export const alovaInst = createAlova({
+ //...
+ // Control whether to use the simulated request adapter through environment variables
+ requestAdapter: process.env.NODE_ENV === 'development' ? mockAdapter : axiosRequestAdapter()
+});
+```
+
+##Typescript
+
+The axios request adapter provides complete type adaptation. The type of method configuration and response data will exactly match the type of axios.
+
+### method configuration
+
+When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `AxiosRequestConfig`, we have removed the items that conflict with the general configuration of the method instance in the type.
+
+```typescript
+/**
+ * axios request configuration parameters
+ * Removed the attributes that conflicted with the method
+ */
+export type AlovaAxiosRequestConfig = Omit<
+ AxiosRequestConfig,
+ | 'url'
+ | 'method'
+ | 'baseURL'
+ | 'headers'
+ | 'params'
+ | 'data'
+ | 'timeout'
+ | 'cancelToken'
+ | 'signal'
+ | 'onUploadProgress'
+ | 'onDownloadProgress'
+>;
+```
+
+### Response data
+
+The response data type of axios is `AxiosResponse`, when you use the axios adapter, you will also get the response data in the same format. In actual use, we usually need to process response data globally. A simple example is as follows:
+
+```typescript
+const alovaInst = createAlova(
+ baseURL: 'https://api.alovajs.org',
+ requestAdapter: axiosRequestAdapter(),
+ responded(response) {
+ // response is automatically inferred as AxiosResponse type
+ return response.data;
+ }
+});
+```
+
+### Error
+
+When axios receives non-20x and 30x response status codes, it will throw an error. In order to contain more information, axios custom-designed the error instance into an `AxiosError` instance instead of a normal Error instance, so when encountering An error will be thrown when there is a server error or a network error, and you can catch it in the global error callback.
+
+```typescript
+const alovaInst = createAlova(
+ baseURL: 'https://api.alovajs.org',
+ requestAdapter: axiosRequestAdapter(),
+ responded: {
+ onSuccess(response) {
+ // response is automatically inferred as AxiosResponse type
+ return response.data;
+ },
+ onError(err: AxiosError) {
+ // err type is any by default, you can cast it to AxiosError
+ //...
+ }
+ }
+});
+```
diff --git a/docs/tutorial/08-request-adapter/04-alova-adapter-taro.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md
similarity index 95%
rename from docs/tutorial/08-request-adapter/04-alova-adapter-taro.md
rename to versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md
index 20fec51a7..da94af45f 100644
--- a/docs/tutorial/08-request-adapter/04-alova-adapter-taro.md
+++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/04-alova-adapter-taro.md
@@ -1,436 +1,443 @@
----
-title: Taro Adapter
-sidebar_position: 40
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info Tips
-
-This plugin only supports the taro application of react 16.8+, vue3 version.
-
-:::
-
-## Install
-
-
-
-
-```bash
-npm install @alova/adapter-taro --save
-```
-
-
-
-
-```bash
-yarn add @alova/adapter-taro
-```
-
-
-
-
-:::warning React-Native Application
-
-If you are develop a React-Native app with Taro, please ensure `metro >= 0.76.0` and enable `resolver.unstable_enablePackageExports` in the `metro.config.js`.
-
-[about unstable_enablePackageExports of metro](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental)
-
-:::
-
-:::warning Dependency precompilation issues
-
-[Dependency precompilation function](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91) has been added in Taro v3.5 beta, and is enabled by default in development mode when you are using the `alova` library and `@alova/scene-react(vue)` may cause the error `` [alova]can not call useHooks until set the `statesHook` at alova instance. ``. This is caused by the prebundle feature repeatedly packaging two different `alova` packages. , turning off the prebundle function at this time can solve this problem.
-
-```js
-// config/dev.ts
-export default {
- // ...
- compiler: {
- type: 'webpack5',
- prebundle: {
- // Close prebundle
- enable: false
- }
- }
-} satisfies UserConfigExport
-
-```
-
-Thanks to [LBinin’s issue](https://github.com/alovajs/scene/issues/63).
-
-This problem has been committed to the Taro team [issue](https://github.com/NervJS/taro/issues/15728) and we look forward to solving this issue.
-
-:::
-
-## Usage
-
-### create alova
-
-Calling **AdapterTaro** will return _Request Adapter_, _Storage Adapter_, and _ReactHook_, so you no longer need to set these three items, and the usage is exactly the same.
-
-
-
-
-```javascript
-import { createAlova } from 'alova';
-import AdapterTaro from '@alova/adapter-taro';
-
-const alovaInst = createAlova({
- baseURL: 'https://api.alovajs.org',
- ...AdapterTaro()
-});
-```
-
-
-
-
-
-```javascript
-import { createAlova } from 'alova';
-import AdapterTaroVue from '@alova/adapter-taro/vue';
-
-const alovaInst = createAlova({
- baseURL: 'https://api.alovajs.org',
- ...AdapterTaroVue()
-});
-```
-
-
-
-
-### Request
-
-The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `Taro.request`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/request/) supported by `Taro.request` in the _config_ of method instance creation
-
-
-
-
-```jsx
-const list = () =>
- alovaInst.Get('/list', {
- // The set parameters will be passed to Taro.request
- enableHttp2: true
- });
-
-const App = () => {
- const { loading, data } = useRequest(list);
-
- return (
- { loading ? Loading... : null }
- The requested data is: { JSON.stringify(data) }
- )
-};
-```
-
-
-
-
-```html
-
- Loading...
- The requested data is: {{ data }}
-
-
-
-```
-
-
-
-
-### Upload
-
-When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `Taro.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name` and `filePath`, these two parameters will be passed to `Taro.uploadFile`, at the same time, you can also specify other parameters in data, and the request adapter will pass them to `formData `in parameters.
-
-Similarly, it is fully compatible with `Taro.uploadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile) supported by `Taro.uploadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance.
-
-
-
-
-```jsx
-const uploadFile = (name, filePath, formData) =>
- alovaInst.Post(
- '/uploadImg',
- {
- name,
- filePath,
-
- // Additional data will be passed into formData of uni.uploadFile
- ...formData
- },
- {
- // Set the request method to upload, and the adapter will call uni.uploadFile
- requestType: 'upload',
-
- // Start upload progress
- enableUpload: true
- }
- );
-
-const App = () => {
- const { loading, data, uploading, send } = useRequest(uploadFile, {
- immediate: false
- });
-
- const handleImageChoose = () => {
- Taro.chooseImage({
- success: chooseImageRes => {
- const tempFilePaths = chooseImageRes.tempFilePaths;
- send('fileName', tempFilePaths[0], {
- extra1: 'a',
- extra2: 'b'
- });
- }
- });
- };
-
- return (
- { loading ? Uploading... : null }
- Upload progress: { uploading.loaded }/{ uploading.total }
-
- {/* ... */}
- )
-}
-```
-
-
-
-
-```html
-
- Uploading...
- Upload progress: {{ uploading.loaded }}/{{ uploading.total }}
-
-
-
-
-
-```
-
-
-
-
-### download
-
-When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `Taro.downloadFile`.
-
-Similarly, it is fully compatible with `Taro.downloadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/download/downloadFile) supported by `Taro.downloadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance.
-
-
-
-
-```jsx
-const downloadFile = filePath =>
- alovaInst.Get('/bigImage.jpg', {
- // Set the request method to download, and the adapter will call uni.downloadFile
- requestType: 'download',
- filePath,
-
- // Start download progress
- enableDownload: true
- });
-
-const App = () => {
- const { loading, data, downloading, send } = useRequest(downloadFile, {
- immediate: false
- });
- const handleImageDownload = () => {
- send('file_save_path');
- };
-
- return (
- { loading ? Downloading... : null }
- Download progress: { downloading.loaded }/{ downloading.total }
-
- {/* ... */}
- );
-}
-```
-
-
-
-
-```html
-
- Downloading...
- Download progress: {{ downloading.loaded }}/{{ downloading.total }}
-
-
-
-
-
-```
-
-
-
-
-## Mock request adapter compatible
-
-When using Taro to develop applications, we may still need to use mock requests, but by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the Taro environment, we need to make the response data of the simulated request adapter compatible with the Taro adapter, so you need to use the **@alova/adapter-taro** package exported `taroMockResponse` as response adapter.
-
-```javascript
-import { defineMock, createAlovaMockAdapter } from '@alova/mock';
-import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro';
-
-const mocks = defineMock({
- //...
-});
-
-// mock data request adapter
-export default createAlovaMockAdapter([mocks], {
- // After specifying the taro request adapter, requests that do not match the simulated interface will use this adapter to send requests
- httpAdapter: taroRequestAdapter,
-
- // Simulate the response adapter, after specifying, the response data will be converted to a taro-compatible data format
- onMockResponse: taroMockResponse
-});
-
-export const alovaInst = createAlova({
- baseURL: 'https://api.alovajs.org',
- timeout: 5000,
- ...AdapterTaro({
- // Control whether to use the simulated request adapter through environment variables
- mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined
- })
- //...
-});
-```
-
-## Typescript
-
-The taro request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of taro.
-
-### method configuration
-
-When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, we have removed the type and method Items that conflict with the common configuration of the instance.
-
-```typescript
-/**
- * Taro.request requests additional parameters
- */
-export type TaroRequestConfig = Omit<
- Taro.request.Option,
- 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete'
->;
-
-/**
- * Taro.uploadFile additional parameter
- */
-export type TaroUploadConfig = Omit<
- Taro.uploadFile.Option,
- 'url' | 'filePath' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete'
->;
-
-/**
- * Taro.downloadFile additional parameters
- */
-export type TaroDownloadConfig = Omit<
- Taro.downloadFile.Option,
- 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete'
->;
-
-/**
- * Merged request configuration parameters
- */
-export type TaroConfig = {
- /**
- * Request type, upload means upload, download means download, not filling means normal request
- */
- requestType?: 'upload' | 'download';
-} & TaroRequestConfig &
- TaroUploadConfig &
- TaroDownloadConfig;
-```
-
-### Response data
-
-Because the taro request adapter is compatible with `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, but their response value types are slightly different, so the response data type is as follows:
-
-```typescript
-type TaroResponse =
- // The response type of Taro.request
- | Taro.request.SuccessCallbackResult
-
- // The response type of Taro.uploadFile
- | Taro.uploadFile.SuccessCallbackResult
-
- // The response type of Taro.downloadFile
- | Taro.downloadFile.FileSuccessCallbackResult;
-```
-
-In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows:
-
-```typescript
-const alovaInst = createAlova({
- baseURL: 'https://api.alovajs.org',
- ...AdapterTaro(),
- responded(response) {
- const { statusCode, data } = response as Taro.request.SuccessCallbackResult;
- if (statusCode >= 400) {
- throw new Error('request error');
- }
- return data || null;
- }
-});
-```
+---
+title: Taro Adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info Tips
+
+This plugin only supports the taro application of react 16.8+, vue3 version.
+
+:::
+
+## Install
+
+
+
+
+```bash
+npm install @alova/adapter-taro --save
+```
+
+
+
+
+```bash
+yarn add @alova/adapter-taro
+```
+
+
+
+
+:::warning React-Native Application
+
+If you are develop a React-Native app with Taro, please ensure `metro >= 0.76.0` and enable `resolver.unstable_enablePackageExports` in the `metro.config.js`.
+
+[about unstable_enablePackageExports of metro](https://facebook.github.io/metro/docs/configuration/#unstable_enablepackageexports-experimental)
+
+:::
+
+:::warning Dependency precompilation issues
+
+[Dependency precompilation function](https://docs.taro.zone/blog/2022/05/19/Taro-3.5-beta#2-%E4%BE%9D%E8%B5%96%E9%A2%84%E7%BC%96%E8%AF%91) has been added in Taro v3.5 beta, and is enabled by default in development mode when you are using the `alova` library and `@alova/scene-react(vue)` may cause the error `` [alova]can not call useHooks until set the `statesHook` at alova instance. ``. This is caused by the prebundle feature repeatedly packaging two different `alova` packages. , turning off the prebundle function at this time can solve this problem.
+
+```js
+// config/dev.ts
+export default {
+ // ...
+ compiler: {
+ type: 'webpack5',
+ prebundle: {
+ // Close prebundle
+ enable: false
+ }
+ }
+} satisfies UserConfigExport
+
+```
+
+Thanks to [LBinin’s issue](https://github.com/alovajs/scene/issues/63).
+
+This problem has been committed to the Taro team [issue](https://github.com/NervJS/taro/issues/15728) and we look forward to solving this issue.
+
+:::
+
+## Usage
+
+### create alova
+
+Calling **AdapterTaro** will return _Request Adapter_, _Storage Adapter_, and _ReactHook_, so you no longer need to set these three items, and the usage is exactly the same.
+
+
+
+
+```javascript
+import { createAlova } from 'alova';
+import AdapterTaro from '@alova/adapter-taro';
+
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterTaro()
+});
+```
+
+
+
+
+
+```javascript
+import { createAlova } from 'alova';
+import AdapterTaroVue from '@alova/adapter-taro/vue';
+
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterTaroVue()
+});
+```
+
+
+
+
+### Request
+
+The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `Taro.request`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/request/) supported by `Taro.request` in the _config_ of method instance creation
+
+
+
+
+```jsx
+const list = () =>
+ alovaInst.Get('/list', {
+ // The set parameters will be passed to Taro.request
+ enableHttp2: true
+ });
+
+const App = () => {
+ const { loading, data } = useRequest(list);
+
+ return (
+ { loading ? Loading... : null }
+ The requested data is: { JSON.stringify(data) }
+ )
+};
+```
+
+
+
+
+```html
+
+ Loading...
+ The requested data is: {{ data }}
+
+
+
+```
+
+
+
+
+### Upload
+
+When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `Taro.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name` and `filePath`, these two parameters will be passed to `Taro.uploadFile`, at the same time, you can also specify other parameters in data, and the request adapter will pass them to `formData `in parameters.
+
+Similarly, it is fully compatible with `Taro.uploadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/upload/uploadFile) supported by `Taro.uploadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance.
+
+
+
+
+```jsx
+const uploadFile = (name, filePath, formData) =>
+ alovaInst.Post(
+ '/uploadImg',
+ {
+ name,
+ filePath,
+
+ // Additional data will be passed into formData of uni.uploadFile
+ ...formData
+ },
+ {
+ // Set the request method to upload, and the adapter will call uni.uploadFile
+ requestType: 'upload',
+
+ // Start upload progress
+ enableUpload: true
+ }
+ );
+
+const App = () => {
+ const { loading, data, uploading, send } = useRequest(uploadFile, {
+ immediate: false
+ });
+
+ const handleImageChoose = () => {
+ Taro.chooseImage({
+ success: chooseImageRes => {
+ const tempFilePaths = chooseImageRes.tempFilePaths;
+ send('fileName', tempFilePaths[0], {
+ extra1: 'a',
+ extra2: 'b'
+ });
+ }
+ });
+ };
+
+ return (
+ { loading ? Uploading... : null }
+ Upload progress: { uploading.loaded }/{ uploading.total }
+
+ {/* ... */}
+ )
+}
+```
+
+
+
+
+```html
+
+ Uploading...
+ Upload progress: {{ uploading.loaded }}/{{ uploading.total }}
+
+
+
+
+
+```
+
+
+
+
+### download
+
+When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `Taro.downloadFile`.
+
+Similarly, it is fully compatible with `Taro.downloadFile`, you can specify [all configuration items](https://taro-docs.jd.com/docs/apis/network/download/downloadFile) supported by `Taro.downloadFile`, if there are additional parameters to be set, please specify them in _config_ of the method instance.
+
+
+
+
+```jsx
+const downloadFile = filePath =>
+ alovaInst.Get('/bigImage.jpg', {
+ // Set the request method to download, and the adapter will call uni.downloadFile
+ requestType: 'download',
+ filePath,
+
+ // Start download progress
+ enableDownload: true
+ });
+
+const App = () => {
+ const { loading, data, downloading, send } = useRequest(downloadFile, {
+ immediate: false
+ });
+ const handleImageDownload = () => {
+ send('file_save_path');
+ };
+
+ return (
+ { loading ? Downloading... : null }
+ Download progress: { downloading.loaded }/{ downloading.total }
+
+ {/* ... */}
+ );
+}
+```
+
+
+
+
+```html
+
+ Downloading...
+ Download progress: {{ downloading.loaded }}/{{ downloading.total }}
+
+
+
+
+
+```
+
+
+
+
+## Mock request adapter compatible
+
+When using Taro to develop applications, we may still need to use mock requests, but by default, the response data of [Mock Request Adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the Taro environment, we need to make the response data of the simulated request adapter compatible with the Taro adapter, so you need to use the **@alova/adapter-taro** package exported `taroMockResponse` as response adapter.
+
+```javascript
+import { defineMock, createAlovaMockAdapter } from '@alova/mock';
+import AdapterTaro, { taroRequestAdapter, taroMockResponse } from '@alova/adapter-taro';
+
+const mocks = defineMock({
+ //...
+});
+
+// mock data request adapter
+export default createAlovaMockAdapter([mocks], {
+ // After specifying the taro request adapter, requests that do not match the simulated interface will use this adapter to send requests
+ httpAdapter: taroRequestAdapter,
+
+ // Simulate the response adapter, after specifying, the response data will be converted to a taro-compatible data format
+ onMockResponse: taroMockResponse
+});
+
+export const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ timeout: 5000,
+ ...AdapterTaro({
+ // Control whether to use the simulated request adapter through environment variables
+ mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined
+ })
+ //...
+});
+```
+
+## Typescript
+
+The taro request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of taro.
+
+### method configuration
+
+When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, we have removed the type and method Items that conflict with the common configuration of the instance.
+
+```typescript
+/**
+ * Taro.request requests additional parameters
+ */
+export type TaroRequestConfig = Omit<
+ Taro.request.Option,
+ 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete'
+>;
+
+/**
+ * Taro.uploadFile additional parameter
+ */
+export type TaroUploadConfig = Omit<
+ Taro.uploadFile.Option,
+ | 'url'
+ | 'filePath'
+ | 'name'
+ | 'header'
+ | 'formData'
+ | 'timeout'
+ | 'success'
+ | 'fail'
+ | 'complete'
+>;
+
+/**
+ * Taro.downloadFile additional parameters
+ */
+export type TaroDownloadConfig = Omit<
+ Taro.downloadFile.Option,
+ 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete'
+>;
+
+/**
+ * Merged request configuration parameters
+ */
+export type TaroConfig = {
+ /**
+ * Request type, upload means upload, download means download, not filling means normal request
+ */
+ requestType?: 'upload' | 'download';
+} & TaroRequestConfig &
+ TaroUploadConfig &
+ TaroDownloadConfig;
+```
+
+### Response data
+
+Because the taro request adapter is compatible with `Taro.request`, `Taro.uploadFile` and `Taro.downloadFile`, but their response value types are slightly different, so the response data type is as follows:
+
+```typescript
+type TaroResponse =
+ // The response type of Taro.request
+ | Taro.request.SuccessCallbackResult
+
+ // The response type of Taro.uploadFile
+ | Taro.uploadFile.SuccessCallbackResult
+
+ // The response type of Taro.downloadFile
+ | Taro.downloadFile.FileSuccessCallbackResult;
+```
+
+In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows:
+
+```typescript
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterTaro(),
+ responded(response) {
+ const { statusCode, data } = response as Taro.request.SuccessCallbackResult;
+ if (statusCode >= 400) {
+ throw new Error('request error');
+ }
+ return data || null;
+ }
+});
+```
diff --git a/docs/tutorial/08-request-adapter/05-alova-adapter-uniapp.md b/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md
similarity index 96%
rename from docs/tutorial/08-request-adapter/05-alova-adapter-uniapp.md
rename to versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md
index 72d1c5d60..890c44aea 100644
--- a/docs/tutorial/08-request-adapter/05-alova-adapter-uniapp.md
+++ b/versioned_docs/version-2.x/tutorial/08-request-adapter/05-alova-adapter-uniapp.md
@@ -1,279 +1,278 @@
----
-title: Uniapp Adapter
-sidebar_position: 50
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-:::info Tips
-
-This plugin only supports vue3 version of uniapp application.
-
-:::
-
-## Install
-
-
-
-
-```bash
-npm install @alova/adapter-uniapp --save
-```
-
-
-
-
-```bash
-yarn add @alova/adapter-uniapp
-```
-
-
-
-
-## Usage
-
-### create alova
-
-Calling **AdapterUniapp** will return _Request Adapter_, _Storage Adapter_, and _VueHook_, so you no longer need to set these three items, and the usage is exactly the same.
-
-```javascript
-import { createAlova } from 'alova';
-import AdapterUniapp from '@alova/adapter-uniapp';
-
-const alovaInst = createAlova({
- baseURL: 'https://api.alovajs.org',
- ...AdapterUniapp()
-});
-```
-
-### Request
-
-The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `uni.request`, you can specify [all configuration items] supported by `uni.request` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api/request/ request.html)
-
-```html
-
- Loading...
- The requested data is: {{ data }}
-
-
-
-```
-
-When using `useRequest/useWatcher` to send a request immediately, it will be executed asynchronously in the `onLoad` hook, so you can access the options data in `methodHandler` as follows:
-
-```javascript
-import { onLoad } from '@dcloudio/uni-app';
-
-let options = {};
-onLoad(opt => {
- options = opt;
-});
-const { loading, data } = useRequest(() => getDetail(options.id));
-```
-
-### Upload
-
-When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `uni.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name`, `filePath or files`, and `file` (if necessary), these 4 parameters will be passed to `uni.uploadFile`, at the same time, you can also specify other parameters besides these 4 parameters in data , the request adapter will pass them into the `formData` parameter.
-
-Similarly, it is fully compatible with `uni.uploadFile`, you can specify [all configuration items] supported by `uni.uploadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#uploadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance.
-
-```html
-
- Uploading...
- Upload progress: {{ uploading.loaded }}/{{ uploading.total }}
-
-
-
-
-
-```
-
-### download
-
-When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `uni.downloadFile`.
-
-Similarly, it is fully compatible with `uni.downloadFile`, you can specify [all configuration items] supported by `uni.downloadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#downloadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance.
-
-```html
-
- Downloading...
- Download progress: {{ downloading.loaded }}/{{ downloading.total }}
-
-
-
-
-
-```
-
-## Mock request adapter compatible
-
-When using uniapp to develop applications, we may still need to use simulated requests, but by default, the response data of [mock adapter (@alova/mock)](/tutorial/request-adapter/alova-mock) is a `Response` instance, That is, it is compatible with the `GlobalFetch` request adapter by default. When used in the uniapp environment, we need to make the response data of the simulated request adapter compatible with the uniapp adapter, so you need to use the **@alova/adapter-uniapp** package exported `uniappMockResponse` as response adapter.
-
-```javascript
-import { defineMock, createAlovaMockAdapter } from '@alova/mock';
-import AdapterUniapp, { uniappRequestAdapter, uniappMockResponse } from '@alova/adapter-uniapp';
-
-const mocks = defineMock({
- //...
-});
-
-// mock data request adapter
-export const mockAdapter = createAlovaMockAdapter([mocks], {
- // After specifying the uniapp request adapter, requests that do not match the simulated interface will use this adapter to send requests
- httpAdapter: uniappRequestAdapter,
-
- // Simulate the response adapter, after specifying, the response data will be converted to a uniapp-compatible data format
- onMockResponse: uniappMockResponse
-});
-
-export const alovaInst = createAlova({
- baseURL: 'https://api.alovajs.org',
- timeout: 5000,
- ...AdapterUniapp({
- // Control whether to use the simulated request adapter through environment variables
- mockRequest: process.env.NODE_ENV === 'development' ? mockAdapter : undefined
- })
- //...
-});
-```
-
-## Typescript
-
-uniapp request adapter provides complete type adaptation, and the type of method configuration and response data will exactly match the type of uniapp.
-
-### method configuration
-
-When creating a method instance, in addition to the general configuration items in the method, you can also use the configuration items in `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, we have removed and method from the type Items that conflict with the common configuration of the instance.
-
-```typescript
-/**
- * uni.request requests additional parameters
- */
-export type UniappRequestConfig = Omit<
- UniNamespace.RequestOptions,
- 'url' | 'data' | 'header' | 'method' | 'timeout' | 'success' | 'fail' | 'complete'
->;
-
-/**
- * uni.uploadFile additional parameters
- */
-export type UniappUploadConfig = Omit<
- UniNamespace.UploadFileOption,
- 'url' | 'name' | 'header' | 'formData' | 'timeout' | 'success' | 'fail' | 'complete'
->;
-
-/**
- * uni.downloadFile additional parameters
- */
-export type UniappDownloadConfig = Omit<
- UniNamespace.DownloadFileOption,
- 'url' | 'header' | 'timeout' | 'success' | 'fail' | 'complete'
->;
-
-/**
- * Merged request configuration parameters
- */
-export type UniappConfig = {
- /**
- * Request type, upload means upload, download means download, not filling means normal request
- */
- requestType?: 'upload' | 'download';
-} & UniappRequestConfig &
- UniappUploadConfig &
- UniappDownloadConfig;
-```
-
-### Response data
-
-Because the uniapp request adapter is compatible with `uniapp.request`, `uniapp.uploadFile` and `uniapp.downloadFile`, but their response value types are slightly different, so the response data type is as follows:
-
-```typescript
-type UniappResponse =
- // The response type of uni.request
- | UniNamespace.RequestSuccessCallbackResult
-
- // The response type of uni.uploadFile
- | UniNamespace.UploadFileSuccessCallbackResult
-
- // The response type of uni.downloadFile
- | UniNamespace.DownloadSuccessData;
-```
-
-In actual use, we usually need to process the response data globally. It is recommended to judge the returned data separately. A simple example is as follows:
-
-```typescript
-const alovaInst = createAlova(
- baseURL: 'https://api.alovajs.org',
- ...AdapterUniapp(),
- responded(response) {
- const { statusCode, data } = response as UniNamespace.RequestSuccessCallbackResult;
- if (statusCode >= 400) {
- throw new Error('request error');
- }
- return data || null;
- }
-});
-```
+---
+title: Uniapp Adapter
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info Tips
+
+This plugin only supports vue3 version of uniapp application.
+
+:::
+
+## Install
+
+
+
+
+```bash
+npm install @alova/adapter-uniapp --save
+```
+
+
+
+
+```bash
+yarn add @alova/adapter-uniapp
+```
+
+
+
+
+## Usage
+
+### create alova
+
+Calling **AdapterUniapp** will return _Request Adapter_, _Storage Adapter_, and _VueHook_, so you no longer need to set these three items, and the usage is exactly the same.
+
+```javascript
+import { createAlova } from 'alova';
+import AdapterUniapp from '@alova/adapter-uniapp';
+
+const alovaInst = createAlova({
+ baseURL: 'https://api.alovajs.org',
+ ...AdapterUniapp()
+});
+```
+
+### Request
+
+The usage method of the request is exactly the same as that used in the web environment. Already fully compatible with `uni.request`, you can specify [all configuration items] supported by `uni.request` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api/request/ request.html)
+
+```html
+
+ Loading...
+ The requested data is: {{ data }}
+
+
+
+```
+
+When using `useRequest/useWatcher` to send a request immediately, it will be executed asynchronously in the `onLoad` hook, so you can access the options data in `methodHandler` as follows:
+
+```javascript
+import { onLoad } from '@dcloudio/uni-app';
+
+let options = {};
+onLoad(opt => {
+ options = opt;
+});
+const { loading, data } = useRequest(() => getDetail(options.id));
+```
+
+### Upload
+
+When `requestType: 'upload'` is set in the _config_ of the method instance, it means to upload the file, the request adapter will call `uni.uploadFile`, and the uploaded file data is placed in the data of the method instance, you need to specify in the data `name`, `filePath or files`, and `file` (if necessary), these 4 parameters will be passed to `uni.uploadFile`, at the same time, you can also specify other parameters besides these 4 parameters in data , the request adapter will pass them into the `formData` parameter.
+
+Similarly, it is fully compatible with `uni.uploadFile`, you can specify [all configuration items] supported by `uni.uploadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#uploadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance.
+
+```html
+
+ Uploading...
+ Upload progress: {{ uploading.loaded }}/{{ uploading.total }}
+
+
+
+
+
+```
+
+### download
+
+When `requestType: 'download'` is set in the _config_ of the method instance, it means to download the file, and the request adapter will call `uni.downloadFile`.
+
+Similarly, it is fully compatible with `uni.downloadFile`, you can specify [all configuration items] supported by `uni.downloadFile` in _config_ of method instance creation (https://uniapp.dcloud.net.cn/api /request/network-file.html#downloadfile), if there are additional parameters to be set, please specify them in _config_ of the method instance.
+
+```html
+
+