简易版前端项目离线方案-接口及页面离线缓存

这篇具有很好参考价值的文章主要介绍了简易版前端项目离线方案-接口及页面离线缓存。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一,要实现的目标

为了避免后端流控、崩溃等异常而无法访问的情况,就需要将接口和页面的静态资源缓存在用户的浏览器本地,这样一来,就算后端服务不可达,前端依旧能有正常的页面显示操作反馈,大部分用户无法感知到系统出现了故障.

二,方案的设计

2.1,前端单独集群部署

这个虽然听起来高大上,其实就是前端服务和后端服务分开部署,这样一来是为了避免后端的服务崩溃掉后,前端web层的响应依旧正常.表现起来就是后端的接口无法响应,但是前端的index.html,js,css等静态资源依旧能够正常访问.
这样部署需要注意的就是配置转发,因为不同服务器前端直接访问后端会跨域.
这部分工作让后端同学完成即可.

2.2,前端页面的缓存设置

这个老生常谈了,一般我们的前端项目,只有index.html响应头设置的cache-control是no-cache,其他的静态资源如图片,页面的js、css、字体文件都是设置的缓存,这个也是nginx上设置,让后端同学整.
这里简单讲下这样设置的原因:

index.html响应头设置的no-cache是为了每次浏览器刷新或者新打开,获取到的index.html都是最新的,这样一来就避免了发版之后,用户还是拿到旧的前端代码的情况.
其他静态资源设置缓存,是为了提高页面的响应速度,原来请求静态资源的路径是浏览器-后端,现在加了缓存就变成浏览器-缓存-后端,只有缓存失效或者不存在的时候,才会建立http链接来获取静态资源,设置缓存便节省了这部分时间.

2.3,前端接口的缓存

这里实际上就两种情况:接口请求成功,写入(更新)缓存,正常展示页面;接口请求失败,拿缓存的数据正常展示页面.

2.4,核心页面的代码合并

假设用户常用的页面有首页和ABC四个页面,那么要是用户进入首页后,前端服务崩了,无法拿到其他页面的静态资源了,就会发生点击无法跳转的情况.
因为我们前端项目多数使用的spa单页面开发,利用的webpack进行打包,这就意味着我们每个页面通常是按需引入,用户才进入首页,是没有获取到另外三个ABC页面的js和css的,这就造成了无法跳转,点击没响应的情况.
为了避免这种情况的发生,就需要将这四个页面的代码单独抽离出来,打包成一个chunk,这样一来,无论访问到哪个页面,都能同步获取到另外三个页面的静态资源,在完全断网后,用户也能最低限度地在这四个页面正常操作.

三,具体的实现

这里主要讲后面两点前端接口的缓存和核心页面代码的打包

3.1,前端接口的缓存

要让用户无感,也就是这些核心页面涉及的接口,可以设置一个需要缓存的接口白名单,

  • 接口名用REQUEST_TXCODE存储,
  • 需要我们手动地拦截错误,不再toast报错给用户看,另外为了避免接口没返回,且没有接口缓存的情况,就需要设置每个接口个性化的返回,来防止页面代码报错(如接口处理res.list.map,如果list不存在的话,会报错list.map is not a function).所以新增RESUTLT_FORMAT字段.
  • 又因为有的是分页接口(只需缓存第一页),所以新增属性REQUEST_TYPE
  • REMARK是为了让开发者好阅读维护代码加的备注
//离线缓存的接口
export const setCacheRequestList = [
  {
    REQUEST_TXCODE: 'TEST0047',
    REQUEST_TYPE: 'NORMAL_REQUEST',
    RESUTLT_FORMAT: { data: { list: [] } },
    REMARK: '获取园区列表'
  },
  {
    REQUEST_TXCODE: 'TEST0120',
    REQUEST_TYPE: 'NORMAL_REQUEST',
    RESUTLT_FORMAT: { data: { LOGIN_MSG: {} } },
    REMARK: '获取首页信息接口'
  },
]

紧接着,对于接口缓存的处理,必然是要在全局做(每个接口单独做会让代码变得很乱),为了不对页面上的业务代码造成修改,我们可以在axios的响应拦截器上做处理,以下为伪代码:

import { setCacheRequestList } from '@/config/whiteList.js';
import { HandleRequestCache } from '@/vue-use/useHandleRequestCache.js';//这个是我写的类,后续会讲
let requestCache = new HandleRequestCache(setCacheRequestList);
export default function request(options) {
  return new Promise((resolve, reject) => {
    // 创建axios实例
    const service = axios.create({
      baseURL: process.env.VUE_APP_baseUrl
    });
    // request拦截器
    service.interceptors.request.use(
      async config => {
        //...请求拦截器处理
        return config;
      },
      error => {
        Promise.reject(error);
      } //请求拦截器的报错处理
    );

    // 响应拦截器
    service.interceptors.response.use(
      res => {
        //判断在缓存白名单中,则忽略报错读取缓存:其实就是将白名单中的接口返回,处理成只走resolve的数据
        res = requestCache.handleResponse(res);

		if (res.data.RESULT === 'N') {
          reject(JSON.stringify(res.data));
        } else {
          resolve(res.data);
        }
      },
      error => {
     	 requestCache.handleResponseErr(error, (res, currentRequest) => {
			//报错的处理
		})
          if (message == 'Network Error') {
             message = '网络开小差了,请稍后重试';
           } else if (message.includes('timeout')) {
             message = '网络开小差了,请稍后重试';
           } else if (message.includes('Request failed with status code')) {
             message =
               '网络开小差了,请稍后重试(' +
               message.substr(message.length - 3) +
               ')';
           }
           Toast({
             message: message,
             type: 'error',
             duration: 2 * 1000
           });
           return reject(error);
    );
    service(options);
  });
}

主要就是这一行代码:

 res = requestCache.handleResponse(res);

他的目的就是实现:白名单内的接口,若接口成功写入缓存,并且把接口的返回原路返回,接口失败,则读取缓存,没有缓存则读取接口默认配置,作为返回.
也就是经过这个方法处理,白名单内的接口,都是正常的响应了(正常后端返回/缓存读取/默认接口配置)
但是呢,对于web层的报错我们还没处理,比如503啦,403啦之类的.
这个就需要我们在响应拦截器的error回调函数中处理:

requestCache.handleResponseErr

这个类的具体代码如下:

//前端首页离线-将首页及部分接口缓存,不再报错处理
import storage from '@/vue-use/useStorage.js';
import common from '@/utils/common.js';
class HandleRequestCache {
  constructor(cacheList = []) {
    this.cacheList = cacheList;
    this.resultFormat = {
      data: { RESULT: 'Y', TRACEID: '10000', data: {} }
    };
  }
  //把请求参数处理成对象
  getUrlToObject(search) {
    let obj = {};
    if (search.indexOf('=') !== -1) {
      let pArr = search.split('&');
      pArr.forEach(e => {
        let kv = e.split('=');
        obj[kv[0]] = kv[1];
      });
    }
    return obj;
  }
  //存储
  setCache(TXCODE, val) {
    let remark = this.cacheList.filter(el => el.REQUEST_TXCODE == TXCODE)[0]
      .REMARK;
    console.log(
      `%c接口${TXCODE}:${remark}成功写入缓存`,
      'background: lightblue; color: #000000'
    );
    let newRes = { data: val };
    storage.setItem(TXCODE, newRes, 'local');
  }
  //获取缓存,需要做判空处理
  getCache(TXCODE, pageCurrent) {
    let remark = this.cacheList.filter(el => el.REQUEST_TXCODE == TXCODE)[0]
      .REMARK;
    console.log(
      `%c接口${TXCODE}:${remark}成功读取缓存`,
      'background: #222; color: #bada55'
    );
    let currentRequest = this.cacheList.filter(
      item => item.REQUEST_TXCODE == TXCODE
    );
    let cache;
    switch (currentRequest[0].REQUEST_TYPE) {
      case 'NORMAL_REQUEST':
        //普通接口
        cache = storage.getItem(TXCODE, 'local')
          ? storage.getItem(TXCODE, 'local')
          : '';
        break;
      case 'PAGE_REQUEST':
        //分页接口只获取第一页,否则取默认
        if (pageCurrent == '1') {
          cache = storage.getItem(TXCODE, 'local')
            ? storage.getItem(TXCODE, 'local')
            : '';
        }
        break;
      default:
        break;
    }
    //缓存-白名单配置-默认配置中谁有值就取谁
    let newRes = !common.isEmptyData(cache)
      ? cache
      : !common.isEmptyData(currentRequest[0].RESUTLT_FORMAT)
      ? currentRequest[0].RESUTLT_FORMAT
      : this.resultFormat;
    newRes.data['cache'] = true; //非正常接口获取
    return newRes;
  }
  //处理接口返回
  handleResponse(res) {
    let { TXCODE, pageCurrent = 0 } = this.getUrlToObject(res.config.data);
    const currentRequest = this.cacheList.filter(
      el => el.REQUEST_TXCODE == TXCODE
    );
    if (currentRequest.length > 0 && res.data.RESULT == 'N') {
      //失败的白名单接口读取缓存
      return this.getCache(TXCODE, pageCurrent);
    } else if (currentRequest.length > 0) {
      //成功的白名单接口存缓存后原样返回
      switch (currentRequest[0].REQUEST_TYPE) {
        case 'NORMAL_REQUEST':
          //普通接口
          this.setCache(TXCODE, res.data);
          break;
        case 'PAGE_REQUEST':
          //分页接口只存储第一页
          if (pageCurrent == '1') {
            this.setCache(TXCODE, res.data);
          }
          break;
        default:
          break;
      }
    }
    return res;
  }
  //处理web层网络报错返回
  handleResponseErr(err, cb) {
    let { TXCODE, pageCurrent = 0 } = this.getUrlToObject(err.config.data);
    const currentRequest = this.cacheList.filter(
      el => el.REQUEST_TXCODE == TXCODE
    );
    let newRes;
    if (currentRequest.length > 0) {
      //失败的白名单接口读取缓存
      newRes = this.getCache(TXCODE, pageCurrent);
    }
    cb(newRes, currentRequest);
  }
}
export { HandleRequestCache };

这样一来,就做到了不动任何页面代码的情况,在项目中新增了接口离线的功能啦.

3.2,核心页面的打包

这一部分就是webpack的配置修改啦,在router中,我们按需引入页面的时候,使用webpack的魔法注释就可以啦:

const Home = () =>
  import(/* webpackChunkName: "home-mine" */ '../views/home/index.vue');
const Mine = () =>
  import(/* webpackChunkName: "home-mine" */ '../views/mine/mine.vue');

这样一来,home和mine页面的js,css都是放在名为home-mine的chunk中啦.文章来源地址https://www.toymoban.com/news/detail-774581.html

到了这里,关于简易版前端项目离线方案-接口及页面离线缓存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 前端部署项目后nginx转发接口404(页面正常)

    目录 1.前言  2. 场景复现: 3.问题的原因: 4.使用nginx一般要注意的小细节:   1.  location / 写在下面,其他的转发如/v1写在上面​编辑  2.如何查看nginx转发请求到哪里了?  3.怎么写自己的前端路径? 5.使用nginx常用的命令: 6.常用nginx配置文件(可以参考,根据自己实际项

    2024年02月08日
    浏览(35)
  • 前端项目部署自动检测更新后通知用户刷新页面(前端实现,技术框架vue、js、webpack)——方案二:轮询去判断服务端的index.html是否跟当前的index.html的脚本hash值一样

    当我们重新部署前端项目的时候,如果用户一直停留在页面上并未刷新使用,会存在功能使用差异性的问题,因此,当前端部署项目后,需要提醒用户有去重新加载页面。 vue、js、webpack 根据打完包之后生成的 script src 的hash值去判断 ,每次打包都会生成唯一的hash值,只要轮

    2024年01月23日
    浏览(32)
  • 前端缓存方式以及区别(vue项目)

           vuex、cookie、sessionStorage、localStorage vuex主要用于vue 组件之间的通信,页面一刷新数据就会消失。 原因:        vuex 是挂载到vue实例上的,相当于全局变量,当页面一刷新,页面重新加载vue 实例,vuex里面的数据被重新赋值。 使用: https://vuex.vuejs.org/zh/ 一般结合

    2024年04月14日
    浏览(55)
  • vue项目发布有缓存,正式环境不更新(解决方案)

    前言:每次测试构建或者打包更新版本发到服务器上,导致偶尔会出现不能及时更新到最新代码,浏览器存在缓存的问题。 定义版本变量: const  Version = new Date().getTime(); // 这里使用的是时间戳 来区分 ,实际上不用加时间戳,webpack内部还自动变化hash值 方法1、 Linux服务器设

    2024年02月08日
    浏览(31)
  • vue项目 前端加前缀(包括页面及静态资源)

    具体步骤 (1)更改router模式,添加前缀 位置:router文件夹下面的index.js (2)实现静态文件加前缀 位置:vue.config.js 静态资源css,js之类的的src或href引用位置会加上这个前缀,会体现在打包后的index.html文件内容 例如 (3)nignx配置

    2024年02月04日
    浏览(43)
  • vue项目切换页面白屏的解决方案

    问题描述 1、页面切换后白屏,同时切换回上一个页面同样白屏 2、刷新后正常显示 3、有警告: Component inside Transition renders non-element root node that cannot be animated 解决方法 Transition中的组件呈现不能动画化的非元素根节点 也就是说,组件内必须有一个根元素 之前: 现在:  原来

    2024年02月08日
    浏览(34)
  • vue项目版本打包更新后文件及浏览器存在缓存问题解决方案

    在vue.config.js中配置output,打包后的文件会带时间戳 在public/static目录下新建version.json文件  在src中新建 utils文件夹 文件夹中新建versionUpdate.js文件  在src文件夹下创建addVersion.js  写法二 修改package.json中scripts中的打包命令 版本号自加使用fs修改文件来实现 具体思路是:在执行

    2024年02月11日
    浏览(37)
  • vue+element项目中页面多个接口异常,只提示一次异常信息

    有时候一个页面会同时调多个接口,但是多个接口异常,需要做提示,那么提示的时候会弹出很多的提示信息,这无疑让体验感降低很多。  所以针对这种情况,我们配合element UI统一做一个异常状态的处理,只能显示一次提示的功能,后续代码调接口的时候 也可以省略去写

    2024年02月04日
    浏览(17)
  • vue项目前端通用埋点方案

    埋点方案主要流程 1、 在 main.js 文件中生成 capol-log-uuid 埋点会话唯一id,并存入 sessionStorage 中 2、在 utils 文件夹下添加 commonLog.js 公共埋点方法类,提供3个方法: 添加埋点函数: CapolLog.pointAdd(dynamicInfo, el) 更新埋点函数: CapolLog.pointUpdate(id, type,updateData) 更新埋点辅助函数:

    2024年02月21日
    浏览(26)
  • 黑马程序员前端 Vue3 小兔鲜电商项目——(八)登录页面

    登录页面的主要功能就是表单校验和登录登出业务。 account password cdshi0080 123456 cdshi0081 123456 cdshi0082 123456 cdshi0083 123456 cdshi0084 123456 cdshi0085 123456 cdshi0086 123456 cdshi0087 123456 cdshi0088 123456 模版代码 在 srcviewsLoginindex.vue 中添加登录页代码: 配置路由跳转 修改 srcviewsLayoutcompon

    2024年02月10日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包