一.问题背景
在开发 H5 项目的时候,本来没有考虑到页面返回停留到原位置这个需求,正常的进行 Vue 路由的跳转,直接进行 Vue 路由的跳转,正常情况如果不使用keep-alive
进行组件缓存的情况下,跳转到一个页面后返回页面是要正常走 Vue 的声明周期进行刷新的,但是在 H5 端用户可能更希望在跳转后返回还是停留在原来的位置,所以就要对之前的代码进行修改来满足这个需求,其实在安卓或者 IOS 壳子中打开一个webview
原来的这个webview
会被正常的缓存到,还会停留在之前的位置,之前的操作也会被缓存到,可以理解为我们新打开了一个浏览器的标签页,新的标签页对原来的标签页面不会造成任何的影响,虽然本质上还是有些差别,所以实现上述的需求的方案有很多,所以就总结一下各种方案的优缺点,以及适用情况。
二.Vuex 实现方案
使用 Vuex 来实现返回停留在原来的位置基本的思路就是当离开这个页面的时候通过scrollTop
来记录下具体的位置然后通过 Vuex 进行存储,然后返回的时候进入页面之前就从 Vuex 中取出来,等到页面加载完毕就滚动到具体位置。
- 优点:不需要缓存 Vue 页面,不会对页面的的正常生命周期造成影响,只需要新增逻辑,不需要修改原来的代码,修改的时候不容易出现 bug。
- 缺点:返回之后原来的页面会重新请求,虽然可也停留在原来的位置,但是当原页面加载完毕位置还是会因为组件的变化而微小变动,页面会出现闪动,原来的操作无法记录,重新请求容易给服务器造成压力。
这个方案其实实现的仅仅就是滚动到原来的位置,并且由于 H5 有的要使用客户端跳转可能会造成失效的情况,如果是最初开发阶段不建议使用此方案,本方案适用于在原来没有这个效果的页面上增加这个效果的时候使用。
js
beforeRouteLeave(to, from, next) {
let position = document.querySelector("#app").scrollTop; //记录离开页面时的位置
if (position == null) position = 0;
this.$store.commit("pageLocation/setPagePostion", position); //离开路由时把位置存起来,
next();
},
computed: {
...mapState({
pagePostionNum: (state) => state.pageLocation.pagePostion,
}),
},
mounted() {
this.isTabRoute();
},
methods: {
isTabRoute() {
if (this.$route.path === "/ETFZone") {
document.querySelector("#app").scrollTop = this.pagePostionNum;
}
},
}
💡 提示:H5 中页面没有可以刷新的按钮所以在 Vuex 的数据不需要担心刷新会丢失,但是在 PC 需要考虑。
三.keep-alive 方案
keep-alive 的方案和 Vuex 的方案思路基本一致,将具体页面缓存,通过keepAlive
在路由 meta 中的配置来选择合适的时间释放缓存,在页面离开的时候通过scrollTop
记录位置,然后当返回这个页面的时候在activated
这个生命周期进行激活滚动到原来的位置。
- 优点:可以缓存页面原来的操作,用户体验感更好,实现思路更加符合主流实现方案。
- 缺点:面对改造的业务会对原来代码进行更改,并且因为加了缓存,很多操作需要激活,容易出现 bug
这个方案适合在项目或者模块开发的初期考虑进取当作主要实现方案,对于维护的代码可能会出现 bug,不容易修改。
js
beforeRouteLeave(to, from, next) {
this.rememberScroll = document.querySelector("#app").scrollTop;
next();
},
// 缓存组件激活时调用
activated() {
document.querySelector("#app").scrollTop = this.rememberScroll;
console.log(document.querySelector("#app").scrollTop, "距离顶部的距离");
},
四.v-show 方案
v-show
方案相对于前两种是最简单的,并且可以一个页面全部梭哈,自己对代码请求更新时机也比较容易把控,不需要考虑位置的记录,激活等等问题。
- 优点:简单易懂,代码可控度较高,不容易出现 bug 等缓存造成的问题。
- 缺点:路由不可用,需要自己编写路由栈,页面过多会造成代码冗余,难以维护。
js
/**
* @class VshowRouter 模拟路由
* @from 当前页面路由
* @to 要跳转的页面路由
* @method addRouter 添加要跳转的路由
* @method navigatorRoute 返回最近的路由
* @method deleteRoute 删除栈顶元素-最近跳转的路由
* @method clearRoute 清空整个路由栈
* @method customRoute 自定义路由跳转-返回将要跳转的路径
*/
class VshowRouter {
constructor() {
this.route = []
}
addRouter(from) {
this.route.push(from)
}
navigatorRoute() {
if (this.route.length >= 1) {
const lastRoute = this.route[this.route.length - 1]
if (lastRoute >= 1) {
this.deleteRoute()
return lastRoute
}
}
}
deleteRoute() {
if (this.route.length > 0) {
this.route.pop()
}
}
clearRoute() {
if (this.route.length > 0) {
this.route.splice(0)
}
}
customRoute(from, to) {
this.addRouter(from)
return to
}
}
export const vShowRouter = new VshowRouter()
在页面中我们就可以使用v-show
来进行控制展示,每展示的页面要进入上述的栈中来控制路由。
js
<main>
<SearchContent
v-show="searchStep === 'one'"
@goSearchUser="navUserResource"
@createOther="createOther"
:obj="obj"
@chioceResultTag="chioceResultTag"
/>
<SearchResult
:searchStatus="searchStatus"
@navUser="navJump"
:ArrayData="ArrayData"
v-show="searchStep === 'two'"
/>
<UserBasicInformation
@goNext="goNextStepTwo"
:userBasic="userBasic"
:userExperience="userExperience"
v-show="searchStep === 'three'"
/>
</main>
虽然v-show
写起来比较简单,但是如果需要支持路由的情况就会变的相对复杂起来,需要自己模拟路由来实现路由栈来解决这个问题,如果页面很简单或者只需要在页面中的某个组件相互切换比较适合选择使用。
五.客户端跳转方案
在 H5 可以通过客户端跳转新开webview
来解决上述的问题,并且不会造成其他的问题,我们来看下这种方案的优缺点。
- 优点:直接跳转新的
webview
不会出现上述问题,仅需要更改跳转为客户端的跳转方法。 - 缺点:参数传递,参数接收不容易监控,打开太多
webview
容易造成性能问题。
如果需要跳转的页面有一页或者一个 Vue 文件可以搞定,其实使用这种方法能够很大程度上解决这个问题,并且可以和v-show
的方案一起使用。
js
toUrlBan(str) {
T.fn.action10061({
url: str,
tzthiddentitle: "0",
TITLETYPE: "1",
});
},
六.问题总结
这篇文章到这里就结束了 🛜,其实面对这类问题应该尽量在开发前将相应的前端业务和用户体验考虑周全,因为如果前期考虑不到,后期新增这些功能的时候会非常的麻烦,并且可能会造成未知的 bug,除了上述的方案其实还有其他的方法但是基本原理都差不多,基本都是缓存和组件隐藏显示,在使用的使用应当根据自己的实际业务选择适合自己的方案。