概述
- 属于专栏-项目难点
- 重要程度:★★★★★
背景
当我们实现一个列表页面,当点击详情,或者点击其他栏目之后,再返回列表页面的时候,我们希望回到离开之前的列表页面,并且回来的时候,滚动条的位置,分页信息,频道信息都是我们离开之前样子,这种情况下我们就需要做页面的缓存
思路
通过使用<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
,通过设置keepAlive
为true
与否,控制页面放到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']
}
}
}