This article, the last in Axios’s tetralogy, shows you how to cache request data in Axios by enhancing the default adapter. So why cache the request data? This is because when the cache is still alive, we can use the cached data directly without having to make a request to get the data from the server, which not only reduces HTTP requests but also improves the user experience by reducing wait time.
Since this article will use the default adapter provided by Axios to cache request data, if you are not familiar with the Axios adapter, it is recommended to read the 77.9K Axios project for lessons. To give you a better understanding of the following content, let’s take a look at the overall flow chart:
The workflow shown in blue is the focus of this article. Next, Bob will start with how to design a cache and take you through the development of caching request data capabilities.
How to design cache
In computing, a cache is a high-speed data storage layer that stores a subset of data, often ephemeral, so that it can be requested again at a later date faster than the primary storage location where the data is accessed. With caching, you can efficiently reuse previously retrieved or computed data. Now that we know what the cache does, let’s design the cache API:
- Get (key) : gets the specified value from the cache
key
Corresponding value; - Delete (key) : deletes a specified object from the cache
key
Corresponding value; - Clear () : clears cached data.
- Set (key, value, maxAge) : saves key/value pairs and sets the maximum cache time
maxAge
The unit is milliseconds.
Based on the above cache API, we can implement a simple cache function as follows:
const MemoryCache = {
data: {},
set(key, value, maxAge) { // Save data
this.data[key] = {
maxAge: maxAge || 0,
value,
now: Date.now(),
};
},
get(key) { // Retrieves the value of the specified key from the cache.
const cachedItem = this.data[key];
if(! cachedItem)return null;
const isExpired = Date.now() - cachedItem.now > cachedItem.maxAge;
isExpired && this.delete(key);
return isExpired ? null : cachedItem.value;
},
delete(key) { // Remove the value of the specified key from the cache.
return delete this.data[key];
},
clear() { // Clear the cached data.
this.data = {}; }};Copy the code
In addition to custom cache objects, you can also use mature third-party libraries such as Lru-cache.
LRU cache elimination algorithm is a common strategy. The full name of LRU is Least Recently Used, that is, we think that the Recently Used data should be “useful”, and the data that has not been Used for a long time should be useless. When the memory is full, the data that has not been Used for a long time should be deleted first.
How to enhance the default adapter
Axios introduces adapters that allow it to support both browser and Node.js environments. For the browser environment, it sends HTTP requests by encapsulating the XMLHttpRequest API, and for the Node.js environment, it sends HTTP requests by encapsulating the built-in HTTP and HTTPS modules of Node.js.
Before we look at how to enhance the default adapter, let’s review the flow of the full Axios request:
Now that we’ve seen the full Axios request flow, let’s take a look at the Axios built-in xhrAdapter adapter, which is defined in the lib/ Adapters /xhr.js file:
// lib/adapters/xhr.js
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
var requestData = config.data;
var requestHeaders = config.headers;
var request = new XMLHttpRequest();
// Omit most of the code
var fullPath = buildFullPath(config.baseURL, config.url);
request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
// Set the request timeout in MS
request.timeout = config.timeout;
// Listen for ready state
request.onreadystatechange = function handleLoad() {... }// Send the request
request.send(requestData);
});
};
Copy the code
Obviously the xhrAdapter adapter is a function object that takes a Config parameter and returns a Promise object. Within the xhrAdapter adapter, you end up using the XMLHttpRequest API to send HTTP requests. In order to cache the requested data, we can consider enhancing the xhrAdapter with higher-order functions.
Pay attention to “the road of full stack Repair fairy” to read the original 4 free e-books of Po Ge (total download 30,000 +) and more than 50 TS series tutorials.
2.1 Define auxiliary functions
2.1.1 Define the generateReqKey function
Before enhancing the xhrAdapter adapter, let’s define a generateReqKey function that generates the request Key based on the current request information.
function generateReqKey(config) {
const { method, url, params, data } = config;
return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}
Copy the code
The request key generated by the generateReqKey function is used as the key of the cache entry, and the corresponding value is the Promise object returned by the default xhrAdapter.
2.1.2 Defining the isCacheLike function
The isCacheLike function is used to determine whether the incoming cache parameter implements the previously defined cache API. Using this function, we allow the user to customize the cache object for each request.
function isCacheLike(cache) {
return!!!!! (cache.set && cache.get && cache.delete && cache.clear &&typeof cache.get === 'function' && typeof cache.set === 'function'
&& typeof cache.delete === 'function' && typeof cache.clear === 'function'
);
}
Copy the code
2.2 Defining the cacheAdapterEnhancer function
To give the user more flexibility over the functionality of the data cache, we have defined a cacheAdapterEnhancer function that takes two arguments:
- Adapter: A pre-enhanced Axios adapter object;
- Options: Cache configuration object. This object supports four properties, which are used to configure different functions:
- MaxAge: The maximum time for the global cache;
- EnabledByDefault: indicates whether caching is enabled. The default value is true.
- CacheFlag: the cacheFlag used to configure cache attributes on the request config object;
- DefaultCache: Used to set the cache object to use.
Now that we know the parameters of the cacheAdapterEnhancer function, let’s look at the implementation:
function cacheAdapterEnhancer(adapter, options) {
const { maxAge, enabledByDefault = true,
cacheFlag = "cache", defaultCache = MemoryCache,
} = options;
return (config) = > {
const { url, method, params, forceUpdate } = config;
letuseCache = config[cacheFlag] ! = =undefined&& config[cacheFlag] ! = =null
? config[cacheFlag]
: enabledByDefault;
if (method === "get" && useCache) {
const cache = isCacheLike(useCache) ? useCache : defaultCache;
let requestKey = generateReqKey(config); // Generate the request Key
let responsePromise = cache.get(requestKey); // Retrieve the response object corresponding to the request key from the cache
if(! responsePromise || forceUpdate) {// If the cache is not hit/invalid or forced to update, the data is re-requested
responsePromise = (async() = > {try {
return await adapter(config); // Use the default xhrAdapter to send requests
} catch (reason) {
cache.delete(requestKey);
throw reason;
}
})();
cache.set(requestKey, responsePromise, maxAge); // Save the response object returned by the request
return responsePromise; // Returns the saved response object
}
return responsePromise;
}
return adapter(config); // Use the default xhrAdapter to send requests
};
}
Copy the code
The above code is not complicated, and the core processing logic is shown below:
2.3 Use cacheAdapterEnhancer function
2.3.1 Create Axios objects and configure Adapter options
const http = axios.create({
baseURL: "https://jsonplaceholder.typicode.com".adapter: cacheAdapterEnhancer(axios.defaults.adapter, {
enabledByDefault: false.Caching is disabled by default
maxAge: 5000.// The cache time is 5s})});Copy the code
2.3.2 Sending requests using HTTP Objects
// Use cache
async function requestWithCache() {
const response = await http.get("/todos/1", { cache: true });
console.dir(response);
}
// Do not use cache
async function requestWithoutCache() {
const response = await http.get("/todos/1", { cache: false });
console.dir(response);
}
Copy the code
In addition to supporting booleans, we can configure the cache object that implements the cache API, as shown in the following example:
const customCache = { get() {/ *... * /}, set() {/ *... * /}, delete() {/ *... * /}, clear() {/ *... * /}};
async function requestForceUpdate() {
const response = await http.get("/todos/1", {
cache: customCache,
forceUpdate: true});console.dir(response);
}
Copy the code
That’s it, how you can enhance the xhrAdapter adapter to enable Axios to cache request data. Because the complete sample code content is more, Po brother will not put the specific code. If you are interested, please visit the following address to browse the sample code.
The complete sample code: gist.github.com/semlinker/b…
Third, summary
This article showed you how to cache the request data in Axios and how to design the cache object. Based on the cacheAdapterEnhancer function defined in this article, you can easily extend the caching capabilities. Now that the Axios tetralogy has been fully updated, here are links to other articles for those who are interested. Please forgive me for my bad writing.
- What can be learned from the 77.9K Axios project
- How does Axios cancel duplicate requests?
- How does Axios implement request retry?
Pay attention to “the road of full stack Repair fairy” read 4 free e-books of Po Ge original (total download 30,000 +) and 11 advanced series of Vue 3 tutorials. If you want to learn TS/Vue 3.0 together, you can add Semlinker on wechat.
Iv. Reference resources
- What can be learned from the 77.9K Axios project
- How does Axios cancel duplicate requests?
- Github – axios-extensions