网络,本地策略(包括缓存,持久化,ORM等等)

解决小程序内嵌h5缓存问题

2020-02-04  本文已影响0人  郁南

该解决方案非原创,出处是收费的,地址就不贴出来了。

下文是结合公司项目重新整理的,分享一下,独乐了不如众乐乐。

缓存问题

大家都知道,浏览器缓存是个非常有用的特性,它能够提升性能、减少延迟,还可以减少带宽、降低网络负荷。关于浏览器的缓存机制可以总结成下面 2 句话:

更进一步,我们可以粗略了解一下强制缓存和协商缓存的运行机理。若强制缓存(Expires 和 Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified/If-Modified-Since 和 Etag/If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回 200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回 304,继续使用缓存。这段文字是想让读者拓展一下知识面,如果想要更输入了解,可以通过上面的一些关键字(强缓存、协商缓存、Expire、Cache-Control 等)去查找更详细的资料。

微信的 web-view 组件就是一个嵌在小程序里的浏览器,它在缓存上并没有完全遵照上述的规则,也即它的缓存并不能及时得到清理。想必下面的操作大家都有尝试过:

无法及时刷新缓存会导致发布了最新的页面,而小程序里仍然是以前的页面,从而会带来许多问题,如前后端的数据不一致,新的特性无法及时起作用,修改的问题没有得到解决等等。这里需要说明一下:web-view 在过一段时间(时间不定,一天或者几小时,无明显规律)是可以进行缓存刷新的,而本 Chat 要解决的是及时刷新的问题。

解决问题

解决思路

浏览器访问资源是通过 URL 地址,如果内嵌 H5 的地址不发生变化,那么 web-view 访问资源会从缓存里取,而缓存里并没有最新的数据,这就导致了服务端的最新资源根本无法到达浏览器,这也就解释了为什么修改 Nginx 的 Cache-Control 配置也无法生效的原因。所以,要想彻底解决及时刷新,必须让 web-view 去访问新的地址。我们假定小程序访问的 URL 地址为:

https://www.yourdomain.com/101/#/index

其中 101 就是构建的一个版本号,每次递增,保证次次不同即可。

先来看一遍大概的流程图

解决小程序内嵌h5缓存问题流程图.png

小程序获取最新版本号

//这里加入同步请求到服务器获取最新路径

onShow: function (options) {
   this.getFEVersion()
},
getFEVersion: function () {
   //下面是利用Promise进行同步调用的写法
   return new Promise(function (resolve, reject) {
     wx.request({
       //下面是本机调试的一个地址,上线时请改成自己服务端的地址
       url: ‘http://192.168.0.168:8090/getFEVersion’,
       data: {},
       method: ‘POST’,
       header: {
         ‘content-type’: ‘application/json’,
       },
       success: function (res) {
         if (res.data.success) {
           const app = getApp();
           //res.data.version 是从服务端返回的最新fe的版本号,即上面的数字101
           app.globalData.feUrl = ‘https://www.yourdomain.com/' + res.data.version + ‘/#/index’
         }
         resolve();
       },
       fail: function (error) {
         console.log(error);
         reject();
       }
     })
   });
 },
// 接口
const H5Rquest = {
  // 获取小程序内嵌h5对应的版本号(对应nginx匹配的文件夹路径)
  getH5Version() {
    return fetch.get(`/api/xxx/${version}/xxx/getH5Version`);
  },
};


// 调用接口
componentDidMount() {
  H5Rquest.getH5Version()
    .then((res) => {
    console.log(res);
    if (!res || !res.data) {
      return WxActions.fnShowToast('无法获取版本号');
    }
    let params = getCurrentUserParams(this.props.chooseChild);
    params = {
      ...params,
      gitlen: baseH5,
      id: this.$router.params.noticeId,
      isEdit: this.$router.params.isEdit,
    };
    const src = `${h5HostName}/${res.data}/#/newsBulletin_announce?${qs.stringify(params)}`;
    console.log(src);// 调试模式可以看到跳转的路径是否跟需要的一致
    this.setState({src});
  })
    .catch((err) => {
    console.log(err);// 报错信息已经统一拦截输出
  });
}


// html代码
render() {
  const {src} = this.state;
  return <WebView src={src} />;
}

Nginx 配置

Nginx 正则规则,~ 表示区分大小写的正则匹配,\d 是数字的匹配的正则表达式,正好可以匹配 101 这样的数字表达式。

server{

      ##其他配置

      ##……

      ##其他配置

      location ~ /\d {

         root /mnt/projects/FE/;

      }

}

在服务器上存放项目的路径为 /mnt/projects/FE/101,下图是 Vue 项目构建好的样子。

000.png

服务端接口

下面是服务端接口的参考代码,我们可以将最新的版本号存入数据库:


@RequestMapping(value = “getFEVersion”, method = RequestMethod.POST)

*public* Object *getFEVersion*(HttpServletRequest request) {

   ResponseVo responseVo = *new* ResponseVo();

   //从数据库中获得最新的版本号

   responseVo.setSuccess(“101”);

   *return* responseVo;

}

总结

到此,我们将小程序发布上线,重启 Nginx 使得配置生效,后续每次构建前端工程,都生成一个新的版本号,如 102、103 等等,将该版本号填入数据库中,后端接口都会及时返回最新版本号,使得小程序总是以最新的路径加载,从而做到 web-view 都能及时的加载最新的 H5 页面。
另外,在链接后面添加时间戳应该也是可以的,具体没验证过,不过哪怕可以,也会造成性能浪费的问题,因为相当于每次进入都要重新下载代码,会导致用户体验不好。代码如下

 const src = `${h5HostName}/index.html?stamp=${new Date().getTime()}#/WatchBaby?${qs.stringify(
      skipParams,
    )}`;
    console.log(src);

综上所述,通过接口生成版本号是比较保险的。

注解

[1]:在 app 的 onShow 钩子函数是小程序启动,或从后台进入前台显示时触发,而且它是所有其他页面 onShow 钩子函数中第一个被执行,因此在这里进行最新版本获取。

[2]:由于需要获得最新版本才能进行其他业务,因此使用同步接口,以此来保证各个生命周期执行的顺序性。

上一篇 下一篇

猜你喜欢

热点阅读