页面样式

页面样式

需求1

从详情页返回列表页,需记录列表页的选择状态和页码

方案:使用localstorage或vuex实现

注:示例代码和业务可能存在依赖,这里只是提供思路

前提:列表页需要实现分页组件

首先在设置一个开关:openroute,实现灰度上线

props: {
  openroute: {
    type: Boolean,
    default: () => (false)
  }
}

在分页组件中存储选择条件和页码

let newPage;
//openroute处理
if (this.openroute) {
  //缓存page
  if (page) { //page存在则缓存page
    localStorage.setItem("lastPath", location.href + "@" + page);
  } else {
    let lastPath = localStorage.getItem("lastPath");
    if (lastPath && lastPath !== 'undefined' && lastPath.split('@')
      && lastPath.split('@')[0] === location.href) { //同一页面时的处理逻辑
      let condition = localStorage.getItem("condition");
      if (condition !== JSON.stringify(params.data)) { //选择条件变更则重置page
        localStorage.setItem("lastPath", location.href + "@1");
      } //选择条件未变更则保持page
    } else { //未存储过数据 或者 页面地址发生变更 时则重置page
      localStorage.setItem("lastPath", location.href + "@1");
    }
  }
  //缓存condition
  localStorage.setItem("condition", JSON.stringify(params.data));
  //获取page
  let lastPath = localStorage.getItem("lastPath");
  if (lastPath && lastPath !== 'undefined' && lastPath.split('@') && lastPath.split('@')[1]) {
    newPage = Number(lastPath.split('@')[1]);
  } else {
    newPage = 1;
  }
} else {
  newPage = page;
}

在列表页面打开开关,并且在created方法中读取存储的数据

u-pagination(@paginated="afterPagination", :params="pagination", :immediate.sync="pagination_immediate" :openroute="true")
//获取缓存的数据
let condition = localStorage.getItem("condition");
if (condition) {
  Object.assign(this.form, JSON.parse(condition))
}

存在的问题

如果请求参数和template中显示的字段不同,会增加回显的工作量。

需求2

由A进入B页面,若点击返回按钮回到A页面,需要缓存A页面;若点击菜单栏回到A页面,不需要缓存A页面。

方案:使用keepalive实现

注:示例代码和业务可能存在依赖,这里只是提供思路

结合include缓存页面

<template>
  <keep-alive include="KeepAlive">
    <router-view :key="key"></router-view>
  </keep-alive>
</template>

<script>
  export default {
    computed: {
      key () {
        return this.$route.fullPath
      }
    }
  }
</script>

在业务页面中增加name: 'KeepAlive',表示该页面应缓存;配合使用activated和deactivated实现一些必要操作

<script>
  export default {
    name: 'KeepAlive',
    activated () {
      if (!this.isCreated) {
        // 执行页面刷新操作
      }
      this.isCreated = false
    },
    data () {
      return {
        isCreated: false,
      }
    },
    created () {
      this.isCreated = true
    },
  }
</script>

如果执行了页面刷新操作,可能会无法保存页码,可以通过以下方式获取正确的页码

let newPage = page
// KeepAlive缓存处理
if (this.$parent.$vnode.tag.indexOf('KeepAlive') !== -1) {
  // 缓存page
  if (page) { // page存在则缓存page
    localStorage.setItem('lastPath', location.href + '@' + page)
  } else {
    let lastPath = localStorage.getItem('lastPath')
    if (lastPath && lastPath !== 'undefined' && lastPath.split('@') &&
      lastPath.split('@')[0] === location.href) { // 同一页面时的处理逻辑
      let condition = localStorage.getItem('condition')
      let newData = params.data
      delete newData.page
      delete newData.size
      if (condition !== JSON.stringify(newData)) { // 选择条件变更则重置page
        localStorage.setItem('lastPath', location.href + '@1')
      } // 选择条件未变更则保持page
    } else { // 未存储过数据 或者 页面地址发生变更 时则重置page
      localStorage.setItem('lastPath', location.href + '@1')
    }
  }
  // 缓存condition
  let cache = params.data
  delete cache.page
  delete cache.size
  localStorage.setItem('condition', JSON.stringify(cache))
  // 获取page
  let lastPath = localStorage.getItem('lastPath')
  if (lastPath && lastPath !== 'undefined' && lastPath.split('@') && lastPath.split('@')[1]) {
    newPage = Number(lastPath.split('@')[1])
  } else {
    newPage = 1
  }
} else {
  newPage = page
}

Vue不提供删除keepalive缓存的逻辑,可以通过以下代码实现

在main.js中注入全局方法

import mixins from './utils/mixins'
Vue.mixin(mixins)

在mixins.js中添加删除keepalive缓存的逻辑

export default {
  beforeRouteLeave (to, from, next) { // keep-alive缓存策略,this是from页面的实例
    try {
      if (from && from.params && from.params.back) { // 如果是返回到to页面,则不清除to页面的缓存
        next()
        return
      } // 如果是 点击菜单栏 或者 修改地址栏 或者 点击浏览器的前进后退按钮 进入到to页面,则需要清除to页面的缓存
      if (to && to.meta.rank && from && from.meta.rank && to.meta.rank === from.meta.rank && // 规定菜单栏点击出现的页面rank为3,其它页面rank为4
        this.$vnode && this.$vnode.data.keepAlive && // KeepAlive为true
        this.$vnode.parent && this.$vnode.parent.componentInstance && this.$vnode.parent.componentInstance.cache && // 有缓存
        this.$vnode.componentOptions // 有配置项
      ) { // 判断是否符合清除to页面缓存的条件
        const cache = this.$vnode.parent.componentInstance.cache
        const keys = this.$vnode.parent.componentInstance.keys
        const key = to.fullPath
        if (key && cache[key]) {
          if (keys.length) { // 清除keys数组中的key数据
            const index = keys.indexOf(key)
            if (index > -1) {
              keys.splice(index, 1)
            }
          }
          delete cache[key] // 清除指定key的缓存
        }
      }
    } catch (e) {
      console.log(e)
    } finally {
      next()
    }
  }
}

存在的问题

如果页面中存在组件或全局监听事件(一般都会存在),可能会导致难以解决的问题。

需求2 - 【目前使用方案】

由A进入B页面,若点击返回按钮回到A页面,需要缓存A页面;若点击菜单栏回到A页面,不需要缓存A页面。

方案:使用vue-router+vue-extends实现

注:示例代码和业务可能存在依赖,这里只是提供思路

在router配置文件中将能通过菜单栏点击出现的页面设置rank=3,其它(如各详情页)为4

因为目前只需为rank为3的页面设置缓存

创建基类BaseCache.vue,并为要缓存的页面实现继承关系

import BaseCache from '@/BaseCache'

export default {
  extends: BaseCache,
  ...
}

在beforeRouteLeave(to, from, next)中按顺序执行以下3步逻辑

  1. 缓存from页面的data到window.cacheData
  2. 判断是否是返回到上一页面,是则return,不执行第3步
  3. 判断rank是否相同,相同则清除to页面的缓存

具体代码如下

beforeRouteLeave (to, from, next) { // 配合缓存策略,this是from页面的实例
  try {
    // 规定菜单栏点击出现的页面rank为3,其它页面rank为4
    if (to && to.meta.rank && from && from.meta.rank) { // 过滤未设置rank的页面
      // rank为3的页面才设置缓存数据
      if (this.$data && from.meta.rank === 3) {
        let cacheData = {}
        // 读取缓存数据
        if (window.cacheData) {
          cacheData = window.cacheData
        }
        // 写入缓存数据
        cacheData[from.path] = this.$data
        window.cacheData = cacheData
      }
      // 如果是返回到to页面,则不清除to页面的缓存
      if (from && from.params && from.params.back) {
        next()
        return
      }
      if (to.meta.rank === from.meta.rank) { // rank相同则清除缓存,说明是点击操作
        let cacheData = {}
        // 读取缓存数据
        if (window.cacheData) {
          cacheData = window.cacheData
        }
        // 如果是同一级页面则to页面的清除to页面的缓存,但不关心删除结果
        delete cacheData[to.path]
        window.cacheData = cacheData
      }
    }
  } finally {
    // 保证一定执行next()方法
    next()
  }
}

在基类中的created方法中读取缓存并回显 & 监听F5刷新操作

具体代码如下:

<template>
  <div></div>
</template>

<script>
  export default {
    name: 'BaseCache',
    mounted () {
      window.onbeforeunload = () => {
        // 刷新页面则清除所有缓存
        window.cacheData = {}
      }
    },
    created () {
      let cacheData = {}
      // 读取缓存数据
      if (window.cacheData) {
        cacheData = window.cacheData
      }
      Object.assign(this.$data, cacheData[this.$route.path])
    }
  }
</script>

最后为了减少内存支出,我们会在登出之后清除所有缓存

存在问题

  1. 如果要缓存的页面存在父子组件关系,目前无法实现缓存;
  2. 如果因缓存数据量过大,导致了性能问题,还需要想一个好的策略。

添加新评论