# 背景
h5项目是一个多页下的单页结构,可以想象每一个营销h5页面是一个多页面;这是大背景,因为架构结构比较复杂,改造起来都比较麻烦(前提)
# 现况
1、在某一期的迭代中,发现页加入了推荐列表从当前路由跳转到发现页详情(同一个页面,不同详情)
解决方案:
- location地址reload
- 将详情页面封装成一个组件,然后新增一个路由,指向这个组件;(两个路由相互跳转)
2、又某一天,产品又提了一个需求说:怎么返回的时候不能记录滚动位置啊,这体验很不好
于是又进行第二波改造,要记录返回的滚动位置,那就是要缓存组件,然后手动设置滚动位置
解决方案
- keep-alive缓存组件 + 栈管理滚动距离
详细:每次进入详情页,将当前id以及滚动距离推到栈中;退出/返回的时候拿出栈顶顶对象,进行滚动距离设置
方案缺陷:虽然可以解决返回滚动的问题,但由于我们keep-alive缓存的组件其实只有两次,第三次的时候其实是缓存组件了,需要在activated加入判断逻辑id是不是变了,获取新的数据,这就导致中间会有一个显示旧数据的问题(这个也好说,加工loading)
到这个阶段,这个页面结构已经残破不堪了;
# 改造方案
改造keep-alive,如果是我们的发现页,将id作为一个key,缓存对应的组件实例
效果
可以移除多余的栈队列,不需要丢到栈里维护滚动行为了;每个组件只需要记录自己的滚动行为,activated的时候滚动到对应位置,同时每个组件实例可以缓存反复使用;
// 部分代码如下
this.$on('discovery:mounted', (compInstance) => {
// 由业务组件进行手动触发,加入缓存cache中
const { $vnode } = compInstance
const { cache, $route } = this
if ($route.query && $route.query.id) {
const cacheId = $route.query.id
if (!cache[cacheId]) {
this.keys.push(cacheId)
this.cache[cacheId] = $vnode
}
}
})
if (cache[key]) {
// 我们的业务组件名称
if (['Detail', 'DetailRedirect'].includes(name)) {
// const cacheId = cache[key].componentInstance.cacheId
const cacheId = this.$route.query && this.$route.query.id
if (cache[cacheId]) {
console.log('命中缓存,返回缓存')
console.log("cacheId: ", cacheId)
vnode.componentInstance = cache[cacheId].componentInstance
} else {
cache[cacheId] = vnode
keys.push(cacheId)
}
} else {
vnode.componentInstance = cache[key].componentInstance
}
} else {
cache[key] = vnode
keys.push(key)
}
# 小结
其实不管是什么方案,都挺复杂的。。。最后是从设计上解决这个问题,上述方案不管是改造成本还是新功能迭代,都有不小的风险;