当前位置:网站首页 > 前端开发 > 正文

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

一,要实现的目标

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

二,方案的设计

2.1,前端单独集群部署

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

2.2,前端页面的缓存设置

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中啦.

到此这篇简易版前端项目离线方案-接口及页面离线缓存的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • 网络爬虫开发(三)-爬虫基础——爬取前端渲染网站的注意事项 & 分析ajax请求并爬取(伪造header)-模拟真实浏览器的请求头2024-11-29 19:27:10
  • 手机哗哩哗哩(b站)视频怎么下载保存到相册2024-11-29 19:27:10
  • 如何使用ES6+特性进行现代前端开发?_前端 es62024-11-29 19:27:10
  • shopify的前端开发教程_shopify前台2024-11-29 19:27:10
  • 前端开发 20 年变迁史_前端开发发展历程2024-11-29 19:27:10
  • 两个前端项目利用iframe进行通信2024-11-29 19:27:10
  • 前端项目ip设置成0.0.0.0的原因2024-11-29 19:27:10
  • 李开复针对马加爵事件写给中国学生的一封信2024-11-29 19:27:10
  • 安卓上按钮文字偏上的问题2024-11-29 19:27:10
  • vw 弹性布局解决方案2024-11-29 19:27:10
  • 全屏图片