vue页面缓存控制

2,558 阅读3分钟

概述

背景

当我们实现一个列表页面,当点击详情,或者点击其他栏目之后,再返回列表页面的时候,我们希望回到离开之前的列表页面,并且回来的时候,滚动条的位置,分页信息,频道信息都是我们离开之前样子,这种情况下我们就需要做页面的缓存

思路

通过使用<keep-alive>组件来包裹<router-view>实现缓存页面,在这里我们还得注意保存下滚动条的位置和防止所有页面都被缓存

实现

1.没做缓存的时候

(a)演示

存在的问题

  • 当前从我的模块切换到主页模块,频道切换回推荐,并且滚动条滚动到第一条记录

(b)代码

为了方便查看,把说所有组件包括路由配置都写到一个html中,包含:

  • App-根组件
  • Layout-布局页面,有tabbar,用于放置主页,我的页面
  • Login-登录页面
  • Home-主页,包含频道导航和文章列表
  • ArticleList-文章列表组件
  • My-我的页面
  • router-路由配置

具体代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue-router/3.5.2/vue-router.min.js"></script>
  <link href="https://cdn.bootcdn.net/ajax/libs/vant/2.12.25/index.min.css" rel="stylesheet">
  <script src="https://cdn.bootcdn.net/ajax/libs/vant/2.12.25/vant.min.js"></script>
  <style>
    .my-page {
      height: 100vh;
      display: flex;
      padding: 0 20px;
      align-items: center;
    }

    .my-page .van-button {
      margin-bottom: 50px;
    }
  </style>
</head>

<body>
  <div id="app">
    <app></app>
  </div>
</body>
<script>
  // 最外边组件,根组件
  const App = {
    template: `<div>
            <router-view></router-view>
        </div>`
  }
  // Layout页面,有tabbar,显示嵌套路由
  const Layout = {  	
    template: `<div>
            <router-view></router-view>
            <van-tabbar route>
                <van-tabbar-item icon="home-o" to="/">主页</van-tabbar-item>
                <van-tabbar-item icon="contact" to="/my">我的</van-tabbar-item>
            </van-tabbar>
        </div>`
  }
  // 登录页面
  const Login = {
    template: `<div>
            <van-nav-bar title="登录" />
            <van-form>
                <van-field label="用户名" />
                <van-field type="password" label="密码"/>
                <div style="margin: 16px;">
                    <van-button round block type="info" to="/">提交</van-button>
                </div>
            </van-form> 
        </div>`
  }
  // 文章列表页面
  const ArticleList = {
    props: {
      // 接受当前频道的信息
      channel: { type: Object, required: true }
    },
    template: `
      <div>
        <van-cell v-for="(item, index) in list" :key="index">
          <template #title>
            <span>{{item.title}}</span>
          </template>
          <template #label>
            <span>{{item.aut_name}}</span>
            <span>{{item.comm_count}}评论</span>
            <span>刚刚</span>
          </template>
        </van-cell>
      </div>
    `,
    data() {
      // 模拟当前文章列表
      let list = []
      for (var i = 0; i < 100; i++) {
        list.push({ aut_name: "张三", title: this.channel.name + "-文章标题-" + (i + 1), comm_count: 0 })
      }
      return {
        list
      }
    }
  }
  // 主页,包含频道导航和文章列表
  const Home = {
    template: `<div>
          <van-tabs color="#1989fa" sticky>
            <van-tab :title="item.name"
              v-for="(item, index) in channels"
              :key="index">
              <article-list :channel="item"></article-list>
            </van-tab>
          </van-tabs> 
        </div>`,
    components: {
      ArticleList
    },
    data() {
      return {
        channels: [
          { id: 0, name: "推荐" },
          { id: 7, name: "数据库" },
          { id: 11, name: "后端" },
          { id: 12, name: "linux" },
          { id: 13, name: "人工智能" },
          { id: 17, name: "前端" },
          { id: 18, name: "python" }
        ]
      }
    }
  }
  // 我的页面
  const My = {
    template: `<div class="my-page">
            <van-button type="info" size="large" round to="/login">登录</van-button>
        </div>`,
  }
  // 路由的配置
  const router = new VueRouter({
    //首页和我的页面
    routes: [
      {
        path: '/', component: Layout, children: [
          { path: '', component: Home },
          { path: '/my', component: My }
        ]
      },
      //登录页面
      {
        path: '/login', component: Login
      }

    ]
  })
  // 挂载vue-router
  Vue.use(VueRouter)
  new Vue({
    el: '#app',
    router,
    components: {
      App
    }
  })
</script>
</html>

(c)预览

链接

2.做了缓存

(a)演示

存在的问题

  • 当前从我的模块切换到主页模块,频道没变,但是滚动条的位置恢复到第一个条记录那里

(b)代码

通过用<keep-alive>组件把<router-view>包裹,实现缓存组件

  // 最外边组件,根组件
  const App = {
    name: 'App',
    template: `<div>
            <!-- 使用keep-alive缓存页面 -->
            <keep-alive>
              <router-view></router-view>
            </keep-alive>
        </div>`
  }
  // Layout页面,有tabbar,显示嵌套路由
  const Layout = {
    name: 'Layout',
    template: `<div>
            <!-- 使用keep-alive缓存页面 -->
            <keep-alive>
              <router-view></router-view>
            </keep-alive>
            <van-tabbar route>
                <van-tabbar-item icon="home-o" to="/">主页</van-tabbar-item>
                <van-tabbar-item icon="contact" to="/my">我的</van-tabbar-item>
            </van-tabbar>
        </div>`
  }

(c)预览

链接

3.解决滚动条的记录和恢复

(a)演示

(b)代码

.article-list 有滚动条,在AriticleList组件当中,监听滚动条的位置,并且做持久化,在activated恢复滚动条的位置,具体代码如下:

    /* 固定文章列表的高度,让滚动条出现在.article-list元素上 */
    .article-list {
      height: 85vh;
      overflow-y: auto;
    }
  // 文章列表页面
  const ArticleList = {
    name: 'ArticleList',
    props: {
      // 接受当前频道的信息
      channel: { type: Object, required: true }
    },
    template: `
      <div class="article-list" ref="listRef">
        <van-cell v-for="(item, index) in list" :key="index">
          <template #title>
            <span>{{item.title}}</span>
          </template>
          <template #label>
            <span>{{item.aut_name}}</span>
            <span>{{item.comm_count}}评论</span>
            <span>刚刚</span>
          </template>
        </van-cell>
      </div>
    `,
    // 变为激活状态
    activated() {
      // 给.article-list元素赋值滚动条的位置数据
      this.$refs.listRef.scrollTop = localStorage.getItem('scrollTop')
    },
    // 变为未激活状态
    deactivated() {
    },
    mounted() {
      //监听滚动条的滚动,保存滚动条的位置
      this.$refs.listRef.onscroll = function (e) {
        localStorage.setItem('scrollTop', e.target.scrollTop)
      }
    },
    data() {
      let list = []
      for (var i = 0; i < 100; i++) {
        list.push({ aut_name: "张三", title: this.channel.name + "-文章标题-" + (i + 1), comm_count: 0 })
      }
      return {
        list
      }
    }
  }

(c)预览

链接

4.解决缓存部分页面-方式1-通过v-if

(a)代码

首先,在路由配置当中添加meta,通过设置keepAlivetrue与否,控制页面放到keep-alive包裹的router-view中还是单独的router-view中,具体代码如下:

  // 最外边组件,根组件
  const App = {
    name: 'App',
    template: `<div>
            <!-- 使用keep-alive缓存页面 -->
            <keep-alive>
              <!-- 需要缓存的页面放这里 -->
              <router-view v-if="$route.meta.keepAlive"></router-view>
            </keep-alive>
            <!-- 不需要缓存的页面放这里 -->
            <router-view v-if="!$route.meta.keepAlive"></router-view>
        </div>`
  }
  // Layout页面,有tabbar,显示嵌套路由
  const Layout = {
    name: 'Layout',
    template: `<div>
            <!-- 使用keep-alive缓存页面 -->
            <keep-alive>
              <router-view v-if="$route.meta.keepAlive"></router-view>
            </keep-alive>
            <!-- 不需要缓存的页面放这里 -->
            <router-view v-if="!$route.meta.keepAlive"></router-view>
            <van-tabbar route>
                <van-tabbar-item icon="home-o" to="/">主页</van-tabbar-item>
                <van-tabbar-item icon="contact" to="/my">我的</van-tabbar-item>
            </van-tabbar>
        </div>`
  }
  // 路由配置
  const router = new VueRouter({
    routes: [
      {
        path: '/', component: Layout, children: [
          // 需要缓存的路由,添加原信息,keepAlive为true
          { path: '', component: Home, meta: { keepAlive: true } },
          { path: '/my', component: My }
        ],
        // 需要缓存的路由,添加原信息,keepAlive为true
        meta: { keepAlive: true }
      },
      {
        path: '/login', component: Login
      }
    ]
  })

(b)预览

链接

5.解决缓存部分页面-方式1-通过includes属性

(a)代码

通过给keep-alive设置include属性控制哪些页面要做缓存,具体代码如下:

  // 最外边组件,根组件
  const App = {
    name: 'App',
    template: `<div>
            <!-- 使用keep-alive缓存页面,通过include缓存部分页面 -->
            <keep-alive :include="cachedViews">
              <router-view></router-view>
            </keep-alive>
        </div>`,
    data() {
      return {
        // 指定要缓存的组件name数组
        cachedViews: ['Layout']
      }
    }
  }
  // Layout页面,有tabbar,显示嵌套路由
  const Layout = {
    name: 'Layout',
    template: `<div>
            <!-- 使用keep-alive缓存页面,通过include缓存部分页面 -->
            <keep-alive :include="cachedViews">
              <router-view></router-view>
            </keep-alive>
            <van-tabbar route>
                <van-tabbar-item icon="home-o" to="/">主页</van-tabbar-item>
                <van-tabbar-item icon="contact" to="/my">我的</van-tabbar-item>
            </van-tabbar>
        </div>`,
    data() {
      return {
        // 指定要缓存的组件name数组
        cachedViews: ['Home']
      }
    }
  }

(b)预览

链接