轻云听书app
17.87MB · 2025-10-15
在开发复杂单页应用(SPA)时,你是否遇到过这些痛点?
本文将系统性地讲解 Vue 中保持页面状态的 4 大策略,覆盖组件卸载与不卸载两种场景,助你打造丝滑的用户体验。
场景 | 解决方案 |
---|---|
组件会被卸载(如路由跳转) | 外部存储 + 路由控制 |
组件不会被卸载(如动态切换) | 内存缓存 + keep-alive |
当用户跳转到其他页面,当前组件被销毁,状态必须“外化”保存。
beforeDestroy
) 保存状态;created
或 mounted
) 恢复状态。// List.vue
export default {
data() {
return {
list: [],
page: 1,
searchQuery: '',
scrollTop: 0
};
},
created() {
// 恢复状态
const saved = localStorage.getItem('listState');
if (saved) {
const state = JSON.parse(saved);
// 只在“返回”时恢复(通过 flag 控制)
if (state.fromDetail) {
Object.assign(this.$data, state.data);
}
}
},
beforeDestroy() {
// 保存状态
const state = {
fromDetail: true, // 标记来源
data: {
list: this.list,
page: this.page,
searchQuery: this.searchQuery,
scrollTop: this.scrollTop
},
timestamp: Date.now()
};
localStorage.setItem('listState', JSON.stringify(state));
},
methods: {
saveScroll() {
this.scrollTop = this.$el.scrollTop;
}
}
}
// 在路由守卫中清除标记
router.beforeEach((to, from, next) => {
if (to.name !== 'List' && from.name === 'List') {
const saved = localStorage.getItem('listState');
if (saved) {
const state = JSON.parse(saved);
state.fromDetail = false; // 下次进入不恢复
localStorage.setItem('listState', JSON.stringify(state));
}
}
next();
});
JSON.stringify()
无法处理 Date
, RegExp
, Function
, Symbol
等类型;vue-router
的 route.state
(类似 React Router);$route.state
读取。// 使用 vue-router 4+ (Vue 3) 或自定义方案
// 跳转时携带状态
this.$router.push({
name: 'List',
state: {
list: this.list,
page: this.page,
searchQuery: this.searchQuery
}
});
// 在目标组件中读取
created() {
const { state } = this.$route;
if (state) {
Object.assign(this.$data, state);
}
}
// 模拟 state 传递
this.$router.push({
name: 'List',
query: {
restore: 'true',
page: this.page,
q: this.searchQuery
}
});
// 接收
created() {
const { restore, page, q } = this.$route.query;
if (restore) {
this.page = Number(page);
this.searchQuery = q;
// 触发数据加载
}
}
当组件只是“隐藏”而非销毁,可直接保留其状态。
<!-- Parent.vue -->
<template>
<div class="container">
<!-- 所有子页面作为全屏组件渲染 -->
<ListComponent v-if="currentView === 'list'" />
<DetailComponent v-if="currentView === 'detail'" />
<EditComponent v-if="currentView === 'edit'" />
</div>
</template>
<script>
export default {
data() {
return {
currentView: 'list'
};
},
provide() {
return {
switchTo: (view) => {
this.currentView = view;
}
};
}
};
</script>
keep-alive
(Vue 官方缓存方案)keep-alive
是 Vue 内置组件,用于缓存动态组件或路由视图;activated
:组件激活时调用;deactivated
:组件失活时调用。<!-- App.vue -->
<template>
<div id="app">
<!-- 缓存需要 keepAlive 的路由 -->
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<!-- 不需要缓存的路由 -->
<router-view v-if="!$route.meta.keepAlive" />
</div>
</template>
// router.js
const routes = [
{
path: '/list',
name: 'List',
component: () => import('@/views/List.vue'),
meta: {
keepAlive: true // 标记需要缓存
}
},
{
path: '/detail/:id',
name: 'Detail',
component: () => import('@/views/Detail.vue')
// 默认不缓存
}
];
// List.vue
export default {
data() {
return {
list: [],
page: 1,
searchQuery: ''
};
},
activated() {
console.log('List 组件被激活');
// 可在此处执行刷新逻辑(如果需要)
},
deactivated() {
console.log('List 组件被缓存');
// 组件状态自动保留
}
}
include
/ exclude
);<keep-alive include="List,Search">
<router-view />
</keep-alive>
max
属性控制缓存数量;<keep-alive :max="5">
<router-view />
</keep-alive>
activated
中需注意避免重复请求。对于复杂状态,建议使用状态管理库:
// store/modules/list.js
const state = {
listStates: {} // key: route fullPath, value: state
};
const mutations = {
SAVE_LIST_STATE(state, { key, data }) {
state.listStates[key] = data;
},
CLEAR_LIST_STATE(state, key) {
delete state.listStates[key];
}
};
// 组件中
computed: {
...mapState(['listStates']),
currentStateKey() {
return this.$route.fullPath;
}
},
created() {
const saved = this.listStates[this.currentStateKey];
if (saved) Object.assign(this.$data, saved);
},
beforeDestroy() {
this.$store.commit('SAVE_LIST_STATE', {
key: this.currentStateKey,
data: pick(this.$data, ['list', 'page', 'searchQuery'])
});
}
方案 | 适用场景 | 是否持久 | 推荐指数 |
---|---|---|---|
LocalStorage | 刷新后需保留 | 是 | ⭐⭐⭐⭐ |
路由传参 | 短期传递,同域跳转 | 否 | ⭐⭐⭐ |
父组件管理 | 简单多视图切换 | 是 | ⭐⭐⭐ |
keep-alive | 路由级缓存(推荐) | 是(内存) | ⭐⭐⭐⭐⭐ |
最佳实践建议:
keep-alive
缓存常用页面;LocalStorage
;Pinia/Vuex
管理。