在微信小程序嵌入的 Vue H5 页面中,实现页面返回不刷新,核心是保留页面的状态和实例,避免返回时重新执行生命周期(如created/mounted)或重新请求数据。由于小程序的 web-view 组件对 H5 的页面栈处理有特殊性,需结合Vue 的路由缓存机制、浏览器会话存储和小程序 web-view 的通信来实现,以下是具体方案:
一、核心原理
- Vue 路由缓存(优先推荐):利用 Vue Router 的
keep-alive组件缓存页面组件实例,返回时直接复用,不重新渲染。 - 状态持久化:将页面数据存储在
sessionStorage/localStorage或 Vuex(配合持久化插件),返回时读取数据恢复状态。 - 小程序 web-view 的页面栈控制:通过小程序与 H5 的通信,避免返回时重新加载 H5 页面。
二、方案一:Vue 路由缓存(keep-alive)
这是 Vue 单页应用(SPA)中最原生的方案,适用于 H5 内部的路由跳转(如/pageA → /pageB,返回/pageA不刷新)。
1. 基本使用(缓存所有页面)
在 Vue 的根路由出口处包裹keep-alive,缓存所有被切换的路由组件:
<!-- App.vue -->
<template>
<div id="app">
<!-- keep-alive 缓存路由组件 -->
<keep-alive>
<router-view />
</keep-alive>
</div>
</template>
此时,路由切换(如从pageB返回pageA)时,pageA的组件实例会被保留,不会重新执行created/mounted,仅触发activated(激活)和deactivated(失活)生命周期。
2. 精准缓存(指定页面缓存)
如果只需缓存部分页面,可通过include/exclude属性控制(需给路由组件设置name):
<!-- App.vue -->
<template>
<div id="app">
<!-- 只缓存name为PageA、PageB的组件 -->
<keep-alive include="PageA,PageB">
<router-view />
</keep-alive>
</div>
</template>
// 路由组件(pageA.vue)
export default {
name: 'PageA', // 必须与include中的名称一致
// ...
}
3. 结合路由元信息(动态缓存)
通过路由的meta字段标记是否需要缓存,更灵活:
// router/index.js(Vue Router配置)
import Vue from 'vue'
import Router from 'vue-router'
import PageA from '@/pages/PageA'
import PageB from '@/pages/PageB'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/pageA',
name: 'PageA',
component: PageA,
meta: { keepAlive: true } // 标记需要缓存
},
{
path: '/pageB',
name: 'PageB',
component: PageB,
meta: { keepAlive: false } // 不需要缓存
}
]
})
<!-- App.vue -->
<template>
<div id="app">
<!-- 根据meta.keepAlive动态缓存 -->
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
</div>
</template>
4. 缓存中的生命周期处理
被keep-alive缓存的组件,不再触发created/mounted,而是触发:
activated:组件被激活时触发(每次进入页面都会执行)。deactivated:组件失活时触发。
示例:在activated中判断是否需要重新请求数据,created中只执行一次初始化:
// pageA.vue
export default {
name: 'PageA',
data() {
return {
list: [] // 页面数据
}
},
created() {
// 只执行一次:初始化静态数据、绑定事件等
console.log('页面首次创建')
},
mounted() {
// 只执行一次:DOM相关操作
console.log('页面首次挂载')
},
activated() {
// 每次进入页面执行:判断数据是否存在,不存在则请求
if (!this.list.length) {
this.fetchData() // 请求数据
}
console.log('页面激活')
},
deactivated() {
// 离开页面执行:清理定时器、取消请求等
console.log('页面失活')
},
methods: {
async fetchData() {
// 模拟接口请求
const res = await new Promise(resolve => {
setTimeout(() => resolve({ list: [1, 2, 3] }), 500)
})
this.list = res.list
}
}
}
三、方案二:状态持久化(应对页面刷新 / 小程序跳转)
如果 H5 页面跳转到小程序原生页面后再返回,或 H5 页面被刷新(如小程序 web-view 重新加载),keep-alive的缓存会失效,此时需要将数据持久化到sessionStorage/localStorage或 Vuex(配合vuex-persistedstate)。
1. 手动使用sessionStorage
在页面数据变化时存储,在页面初始化时读取:
// pageA.vue
export default {
data() {
return {
list: []
}
},
created() {
// 初始化时读取sessionStorage中的数据
const cacheData = sessionStorage.getItem('pageA_list')
if (cacheData) {
this.list = JSON.parse(cacheData)
} else {
this.fetchData() // 无缓存则请求数据
}
},
methods: {
async fetchData() {
const res = await new Promise(resolve => {
setTimeout(() => resolve({ list: [1, 2, 3] }), 500)
})
this.list = res.list
// 存储数据到sessionStorage
sessionStorage.setItem('pageA_list', JSON.stringify(this.list))
},
// 可选:离开页面时清理缓存(根据需求)
beforeDestroy() {
// sessionStorage.removeItem('pageA_list')
}
}
}
2. Vuex 持久化(推荐多页面共享数据)
使用vuex-persistedstate插件将 Vuex 的状态持久化到sessionStorage/localStorage:
# 安装插件
npm install vuex-persistedstate --save
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
pageAData: [] // 存储pageA的数据
},
mutations: {
SET_PAGEA_DATA(state, data) {
state.pageAData = data
}
},
actions: {
fetchPageAData({ commit }) {
// 模拟接口请求
return new Promise(resolve => {
setTimeout(() => {
const data = [1, 2, 3]
commit('SET_PAGEA_DATA', data)
resolve(data)
}, 500)
})
}
},
// 配置持久化:存储到sessionStorage(默认localStorage)
plugins: [
createPersistedState({
storage: window.sessionStorage, // 会话存储,关闭小程序后失效
paths: ['pageAData'] // 只持久化pageAData字段
})
]
})
// pageA.vue
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState(['pageAData'])
},
created() {
if (!this.pageAData.length) {
this.fetchPageAData()
}
},
methods: {
...mapActions(['fetchPageAData'])
}
}
四、方案三:小程序 web-view 与 H5 的通信(应对跨端跳转)
如果 H5 页面需要跳转到其他 H5 页面(跨域名)或小程序原生页面,返回时避免刷新,需通过小程序的web-view组件的通信能力控制。
1. H5 跳转到小程序原生页面(返回时保留 H5 状态)
- 步骤 1:H5 中调用微信 JS-SDK 的
wx.miniProgram.navigateTo跳转到小程序原生页面。 - 步骤 2:小程序原生页面返回时,使用
wx.navigateBack回到 H5 页面,此时 H5 页面的keep-alive缓存或sessionStorage数据仍存在,不会刷新。
// H5页面中(需先引入微信JS-SDK)
wx.miniProgram.navigateTo({
url: '/pages/nativePage/nativePage' // 小程序原生页面路径
})
2. H5 跳转到另一个 H5 页面(同域名 / 跨域名)
- 同域名:使用 Vue Router 的
push/replace跳转,结合keep-alive缓存(方案一)。 - 跨域名:通过
wx.miniProgram.postMessage通知小程序,由小程序的web-view组件重新加载新的 H5 地址,返回时通过sessionStorage恢复状态(方案二)。
五、注意事项
- 小程序 web-view 的缓存机制:小程序的
web-view组件默认会缓存 H5 页面的资源,但页面的 JS 上下文会在跳转后销毁,因此keep-alive仅适用于 H5 内部的路由跳转,跨端跳转需用持久化方案。 - sessionStorage 的限制:
sessionStorage与页面的会话关联,关闭小程序后会失效,若需长期保存数据,可使用localStorage(需注意数据清理)。 - 数据清理:缓存的数据需在适当时机清理(如用户退出页面、数据过期时),避免占用内存或展示旧数据。
- Vue3 的调整:若使用 Vue3,
keep-alive的使用方式略有变化(需配合<router-view v-slot="{ Component }">),但核心原理一致。
总结
- H5 内部路由跳转:优先使用
keep-alive缓存组件,结合activated/deactivated生命周期处理数据。 - H5 页面刷新 / 跨端跳转:使用
sessionStorage/Vuex 持久化存储数据,恢复页面状态。 - 小程序与 H5 通信:通过微信 JS-SDK 实现跨端跳转,返回时利用小程序的页面栈保留 H5 状态。
以上方案可根据实际场景组合使用,确保页面返回时不刷新且状态一致。


被折叠的 条评论
为什么被折叠?



