我在VUE中keep-alive遇到的问题及解决方法

1,821 阅读4分钟

最近用VUE做商城开发遇到了一个场景,

有三个页面分别是:

1.分类页面:展示了很多入口,如手机(id:1)、电视(id:2)、耳机(id:3)、电脑(id:4)等等..

2.商品列表页:点击分类之后查询对应的商品列表

3.商品详情:点击商品列表中的商品进入详情页,查看当前商品的详细信息

目标

1.分类页面 --> 进入商品列表 商品列表展示最新数据

2.商品详情 --> 返回商品列表 商品列表展示历史数据(如:我点击的商品列表第50个商品进入详情页,点击返回后列表页还要停留在第50个的位置)

实现

首先用 keep-alive 来进行缓存,在router配置中,对商品列表对应的路由的meta里面添加keepAlive: true, 用这个属性表示这个页面需要被缓存,

    // 在商品列表的路由配置添加
    meta: {
      keepAlive: true,
    },

然后再更改App.vue文件

    // 通过路由里面meta.keepAlive属性判断是否需要缓存组件
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive"></router-view>

但是这样会让商品列表页面的被一直缓存,每次点击分类进入列表看到的永远都是一样的数据。显然只是这样处理是不行的。

后面发现被keep-alive的组件created、mounted等生命周期只能被触发一次,这个时候需要用到 keep-alive 组件的专用生命周期 activated ,每次进入被keepAlive的页面都会触发这个生命周期。

因为分类页面进入商品列表是会传递一个ID的,所以就在activated里面通过ID判断页面数据是否需要刷新

这是分类页面进入商品列表的方式

  // 分类页面
  // 点击分类进入商品列表 传递了一个分类ID
  this.$router.push({ path: '/productlist', query: { id } });
  // productlist 商品列表页面
  data() {
    return {
      id: '',   // 接收分类页面获得的不同分类ID,通过ID去查找对应的商品列表
    };
  },
  methods: {
      getdata() {
      //4.获取商品列表数据...
      },
  },
  created() {
      // 1.首次加载页面时记录分类ID
      this.id = this.$route.query.id;
  },
  activated() {
    // 2.匹配当前页面缓存的ID和接收的ID是否匹配,如果不匹配就重新请求数据
    if(this.$route.query.id != this.id) {
      // 3.更新id
      this.id = this.$route.query.id;
      this.getdata();
    }
  },

这里最重要的就是将获取页面数据的操作放在 activated() 里面, created() 只会执行一次,用于获取id,后续是否更新页面上的数据就根据 activated 里面的if条件判断。 这个时候的逻辑就比较完善了,但是像下面的第三种的情况还是不完善。点击相同的分类不会刷新商品列表页。

1点击分类A --> 商品列表 --> 商品详情 --> 点击返回商品列表 正常缓存了商品列表没问题

2点击分类A --> 商品列表 --> 商品详情 --> 点击返回回到分类页 --> 点击分类B 正常刷新商品列表没问题

3点击分类A --> 商品列表 --> 商品详情 --> 点击返回回到分类页 --> 点击分类A 商品列表未刷新,有点问题

延续之前的思路

在分类页进入商品列表时

这是之前的跳转

      // 点击分类进入商品列表
      this.$router.push({ path: '/productlist', query: { id } });

将其改成

      // 生成一个随机字符串
      let key = Math.random().toString(36).substr(2);
      // 点击分类进入商品列表,额外传递了一个key
      this.$router.push({ path: '/productbrandlist', query: { id, key } });

这样就每次在分类页面进入商品列表除了传了ID之外,还传递了一个随机字符串key(其实这里的key直接用时间戳更好)

然后再改一下商品列表页

  data() {
    return {
      id: '',   // 接收分类页面获得的不同分类ID,通过ID去查找对应的商品列表
    };
  },
  methods: {
      getdata() {
      //获取商品列表数据...
      },
  },
  created() {
      // 首次加载页面时记录分类ID
      this.id = this.$route.query.id;
      // --------这里是新加的-------
      this.key = this.$route.query.key;
  },
  activated() {
    // 匹配当前页面缓存的ID和接收的ID是否匹配,如果不匹配就重新请求数据
    // --------新加key的匹配判断-------
    let id = this.$route.query.id;
    let key = this.$route.query.key;
    if((id != this.id) || (key != this.key)){
      this.getdata();
    }
  },

这样改了后,整个逻辑就正确了,商品详情页面返回的是缓存状态的商品列表,分类页面进入商品列表一直都是最新的商品列表。

网上查阅的类似问题,差不多都是用的 keep-alive + include,但是这种方式我用着始终有问题,不知道是我没用对,还是这种方式不适合我这种需求。

不过换一种思路将商品列表直接缓存,然后通过不同参数判断是否要重新加载数据。实现起来比较简单,也比较好理解。