demand

When submitting the mini program for review, there is an experience evaluation, and the product asks us to optimize the mini program according to the experience evaluation report of the mini program.

One of them is the optimization of network requests, which gives us a big problem.

The document explains that the same url request does not contain the same contents larger than 128KB twice within 3 minutes

When you see this problem, the first thing that comes to mind is to add cache-control to the response header. After testing, it turns out that the applet does not support network request caching. Wx. request does not support HTTP caching

Since network request caching is not officially supported, you’ll have to figure it out yourself.

Let’s start with requirements: the same request can only be requested once in 3 minutes.

Analysis of the

Analysis:

  1. Only need to doGETNetwork cache of requests.
  2. How to control the cache time.
  3. How do I know that once I’ve cached it3Has the request data been updated on the server within minutes?
  4. submitGETCheck local cache before requesting
  5. What about paging? What about caching

The first two are easier to implement. Although applets do not support network request caching, we can use cache-control to implement this function.

When the server processes the GET request, it adds the response header cache-control. Max-age =180 if the request needs to be cached, and no-cache if the request does not need to be cached. The front-end does its own front-end caching based on the response header information.

The difficulty is how does the front end know if the server data has been updated, and if the server data has been updated, the front end still uses caching, which is problematic.

After some reflection, we found that after the front-end submits data, the corresponding GET request data will be updated, that is, whenever the front-end submits data, the cache should be cleared.

One difficulty is that when the front end submits data, the front end does not know which GET requests will update the data, so we have not solved this problem. My approach is rather rough: as soon as the front end submits data, all caches will be cleared. This is a problem of treating the symptoms rather than the root cause.

And then finally what about paging requests, our paging is going to be in the request header range, and we’re going to have a response header content-range on the back end, so if we have a cached key here we’re going to have a rang, which is a rang– API

implementation

Corporate projects encapsulate HTTP requests

  1. Intercept the request if yesGETRequest, check the cache,
    • If the cache has not expired, the cache is returned and no more requests are made
    • If the cache expires, send the request
if (request.method.toLowerCase() === "get") {// Param requests information
  const cache = this.handleCatchControl(request)
  if(! cache.isRequest)return this.listener.onApiResponse(request, 200, cache.data), sequence;   // Return the cache to the corresponding request
}
Copy the code
  1. Caching network requests
// param response header, context, response data
this.setCatchControl(headers, context, response.data)
Copy the code
  1. Two utility functions
    • Dealing with network caching
    • Setting network Cache
  • Setting up network Requests
    1. GETRequests to cache data, other requests to flush data
    2. Data format:
    // If multiple 'GET' requests are made at the same time, the cached data needs to be concatenated
    ApiAgent.cacheData = Object.assign(ApiAgent.cacheData,{
      [context.request.url]: {    //api
        data,   // Response data
        expireTime: Number(cacheControl.split("=") [1] + '000'),   // Expiration time
        cacheTime: new Date().getTime(),    // Cache time}})Copy the code
// param response header, context, response data
setCatchControl(responseHeader: any, context: any, data: any) {
  if (context.request.method.toLowerCase() === "get") {
    const headers = HandleHeaders.get(responseHeader)
    const cacheControl = headers["cache-control"]
    if(cacheControl && cacheControl ! = ="no-cache") {
      const key = this.handleRange(responseHeader, context.request.url)
      ApiAgent.cacheData = Object.assign(ApiAgent.cacheData,{
        [key]: {
          data,
          expireTime: Number(cacheControl.split("=") [1] + '000'),
          cacheTime: new Date().getTime(),
        }
      })
    }
  } else {
    ApiAgent.cacheData = {}
  }
}
Copy the code
  • Dealing with network caching
    1. Determine whether the cache exists
    2. Check whether the cache has expired. When setting the cache, compare the current time with the cache time to see whether the cache time is less than the expiration time
// Param requests information
handleCatchControl(request): any {
  const cacheArr = ApiAgent.cacheData

  // No cache
  if (Object.keys(cacheArr).length === 0)
    return { isRequest: true }

  // Paging key processing
  const key = this.handleRange(request.headers, request.url)

  // Cache processing
  let cache = {}
  Object.keys(cacheArr).forEach(cacheArrKey= > {
    if (cacheArrKey === request.url) {
      cache = cacheArr[cacheArrKey]
    }
  })
  const newDate = new Date().getTime()
  
  / / a cache
  if (newDate - cache.cacheTime < expireTime){
    return { isRequest: false.data: cache.data }
  }

  // Cache expires
  return { isRequest: true}}Copy the code
  1. To deal withrangeRequest and responserangeDeal with
    • The request ofrangeFormat:range: objects=1-10
    • The response ofrangeFormat:content-range: objects 1-10/58
handleRange(headers: any, url: string): string {
  if(! headers)return url
  
  const header = HandleHeaders.get(headers)
  const reg = /(=|\s)([0-9]*[\-][0-9]*)\/? /
  let range: string = ""
  
  if (header.range) {
    range = header.range.match(reg)[2]}else if (header["content-range"]) {
    range = header["content-range"].match(reg)[2]}if(range)
    return `${range}--${url}`
  return url
}
Copy the code
  1. The response header is all lowercase. In small programs, the inability to determine the case of the response header will result in an error, so the response header is handled uniformly
class HandleHeaders {
  static get(headers: { [key: string]: string }) {
    const headersData: any = {}
    Object.keys(headers).forEach(key= > {
      headersData[key.toLowerCase()] = headers[key]
    })
    return headersData
  }
}
Copy the code

conclusion

One thing I don’t say is, where is this cache stored?

Neither localStorage nor GlobalApp are used, using static properties of the class.

This has three advantages:

  1. uselocalStorageData is not easy to clear, and later maintainability is poor
  2. Cache on theglobalappThere is no direct connection to the request
  3. There is no need to manually clear the cache when exiting the applet

I encountered a pit when using it, because I did not understand: class can save data, can not save state, but the object of class can save both data and state.

Finally, there is a lot of room for optimization.

In addition, wechat TTXBg180218 can be added for communication: