小程序中解决网络请求缓存

需求

提交小程序审核时,有一个体验测评,产品让咱们根据小程序的体验测评报告去优化小程序。前端

其中有一项是网络请求的优化,给咱们出了很大的难题。小程序

文档中是这样解释的:3分钟之内同一个url请求不出现两次回包大于128KB且如出一辙的内容api

看到这个问题的时候,首先想到的是在响应头上加上cache-control,通过测试发现小程序并不支持网路请求缓存。搜索发现官方明确答复,小程序不支持网络请求缓存:wx.request不支持http缓存缓存

既然官方不支持网络请求缓存,那只能本身想办法解决这个问题了。微信

先来看一下需求:3分钟内,同一请求只能请求一次。网络

分析

分析:app

  1. 只需作GET请求的网络缓存。
  2. 缓存时间如何控制。
  3. 作了缓存以后,如何知道3分钟,这个请求在服务端数据有没更新。
  4. 提交GET请求前,先检查本地有没有缓存

前两点比较好实现,虽然小程序不支持网络请求缓存,但咱们仍是能够利用cache-control来实现这个功能。函数

首先网络请求需不须要情缓存统一交给服务端去作,服务端在处理GET请求时,统一加上响应头cache-control,若是须要缓存就用max-age=180,若是不须要作网络请求就用no-cache。前端根据响应头信息本身作前端缓存。工具

其中的难点是前端如何知道服务端数据有没更新,若是服务端数据更新了,前端仍是使用缓存这是有问题的。测试

通过一番思考后发现,前端提交数据后,相应的GET请求数据会更新,也就是说前端只要有数据提交,就应该把缓存清空。

这有一个难点,当前端提交数据时,前端是不知道哪些GET请求会所以更新数据,因此这个问题咱们没有解决,个人方法比较粗暴:只要前端提交了数据,就将全部缓存清空。这是一个治标不治本的问题。

实现

公司项目封装了HTTP请求

  1. 拦截请求,若是是GET请求,检查缓存,

    • 若是缓存没过时,将缓存返回出去,再也不发请求
    • 若是缓存过时,发请求
if (request.method.toLowerCase() === "get"){
  // param 请求信息
  const cache = this.handleCatchControl(request)
  if (!cache.isRequest)
    return this.listener.onApiResponse(request, 200, cache.data), sequence;   //将缓存返回给对应的请求
}
  1. 缓存网络请求
// param 响应头,上下文,响应数据
this.setCatchControl(headers, context, response.data)
  1. 两个工具函数

    • 处理网络缓存
    • 设置网络缓存
  • 设置网络请求

    1. GET请求缓存数据,其余请求清空数据
    2. 数据格式:
    //若是同时发起多个`GET`请求,须要拼接以前缓存数据
    ApiAgent.cacheData = Object.assign(ApiAgent.cacheData,{
      [context.request.url]: {    //api
        data,   //响应数据
        expireTime: Number(cacheControl.split("=")[1] + '000'),   //过时时间
        cacheTime: new Date().getTime(),    //缓存时间
      }
    })
// param 响应头,上下文,响应数据
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") {
      ApiAgent.cacheData = Object.assign(ApiAgent.cacheData,{
        [context.request.url]: {
          data,
          expireTime: Number(cacheControl.split("=")[1] + '000'),
          cacheTime: new Date().getTime(),
        }
      })
    }
  } else {
    ApiAgent.cacheData = {}
  }
}
  • 处理网络缓存

    1. 判断缓存是否存在
    2. 判断缓存有没过时,在设置缓存时,比对当前时间和缓存时间,是否小于失效时间
// param 请求信息
handleCatchControl(request): any {
  const cacheArr = ApiAgent.cacheData
  if (Object.keys(cacheArr).length === 0)
    return { isRequest: true }
  let cache = {}
  Object.keys(cacheArr).forEach(cacheArrKey => {
    if (cacheArrKey === request.url) {
      cache = cacheArr[cacheArrKey]
    }
  })
  const newDate = new Date().getTime()
  if (newDate - cache.cacheTime < expireTime){
    return { isRequest: false, data: cache.data }
  }
  return { isRequest: true}
}
  1. 响应头所有变成小写,在小程序中,没法肯定响应头的大小写会致使报错,因此统一处理响应头
class HandleHeaders {
  static get(headers: { [key: string]: string }) {
    const headersData: any = {}
    Object.keys(headers).forEach(key => {
      headersData[key.toLowerCase()] = headers[key]
    })
    return headersData
  }
}

总结

有一点没有说,就是这个缓存是保存在哪里的?

既没有用localStorage,也没有用globalapp,用的是类的静态属性。

这样作有3个好处:

  1. 使用localStorage数据很差清除,后期可维护性也较差
  2. 缓存挂在globalapp和请求无直接联系
  3. 无需在退出小程序时手动清理缓存

我在使用时遇到一个坑,是由于本身没有理解:类能保存数据的,不能保存状态,但类的对象是既能够保存数据,也能够保存状态的。

最后,此方法仍是有很大的优化空间。

另外可添加微信ttxbg180218交流

相关文章
相关标签/搜索