阅读 697

小tips-一种移动端模拟实现返回拦截的方案

移动端作为互联网重要入口,切图仔前端工程师开发移动端页面早已是司空见惯的事了,并且其中大部分都是内嵌于客户端(app,小程序)的 h5页面,即 webview

有次被同事问到一个问题,他有个需求,是一个内嵌在客户端app内的页面,有个功能点是拦截客户端的返回操作,实现页面内弹层的显示与隐藏,拦截点包括 app页面上自带的返回按钮以及安卓机的物理按钮,类似于下面这种:

点击购物车,则购物车元素从页面底部弹起显示,点击遮罩层,则弹层关闭隐藏,这是很正常的操作,而除此之外,还需要实现的一个功能是,点击屏幕返回键或者安卓机的物理返回键,也能够关闭弹层,并且保证页面不发生跳转

我听了微微一笑,问他,哪个傻叉 PM提的需求?,h5怎么拦截 app甚至是物理按钮的操作?你当时没有怼他吗?客户端那边有提供拦截返回的 sdk接口吗?同事回答 sdk没有提供这个能力,他觉得不好实现,但 PM坚持要做这个功能

于是我抱着反正不是我的需求,我天马行空章口就来瞎提建议也没关系助人为乐的态度思考了一会,谁知还真让我想到了一个方案(我不确定以前是不是在什么地方看到过,总之就是想到了),回来验证了一下,确实是可行的

关键点在于,利用返回操作会触发路由改变的特性来模拟达到拦截的效果,并不是真的监听或者拦截到了屏幕返回键或者物理返回键的点击

路由配置

假设需要进行模拟拦截返回操作的主页面路由为 /physicsBack,其下有个子路由 /physicsBack/footerModal,当路由为 /physicsBack时就只显示页面,当路由为 /physicsBack/footerModal时,就在 /physicsBack上弹起弹层

这里的 /physicsBack/footerModal说起来是子路由,但实际上我们只是想利用其作为路由的一个能力——即拦截返回操作,所以实际上并不真的需要为这个路由配置一个页面,你当然也可以这么做,最后也能实现效果,但未免麻烦了些

这里我将 /physicsBack/physicsBack/footerModal全部指向同一个页面,即主页面,然后通过对路由的监听,来控制弹层的显隐

路由配置如下:

 const router = new VueRouter({
  routes: [{
    path: '/physicsBack/(footerModal)?',
    component: physicsBack
  }]
 })
复制代码

/physicsBack/(footerModal)?同时匹配 /physicsBack/physicsBack/footerModal,所以无论路由是 /physicsBack 还是 /physicsBack/footerModal,都将指向 physicsBack这个页面,达到即使路由在这两个中来回切换,但页面也毫无变化的目的

路由监听

虽然在路由 /physicsBack/physicsBack/footerModal中切换不会引起页面的切换,自始至终都停留在 physicsBack组件上,但却可以在 physicsBack组件中对路由进行监听,进而根据监听到的路由变化来控制弹层的显隐:

watch: {
  $route (to, from) {
    this.manageFooterModal(to.path, from.path)
  }
}
// ...
manageFooterModal (toPath, fromPath) {
  if (toPath === '/physicsBack/footerModal') {
    this.visible = true
  } else if (fromPath === '/physicsBack/footerModal') {
    this.visible = false
  }
}
复制代码

toPath/physicsBack/footerModal,表示将切换到这个路由,前面已经规定了,当路由切换到这个位置时,显示弹层;

fromPath/physicsBack/footerModal,表示从有弹层的页面回退或者跳走,则关闭弹层

这里你不用 watch也是可以的,用 vue-router提供的路由守卫钩子函数(beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave)也能够达到同样的效果,这些钩子函数只是进一步简化了流程,但本质都还是一样的,目的都是实现对路由的监听与控制,所以你哪怕不想依赖于框架提供的能力,通过设置原生监听函数 window.addEventListener('hashchange', callback)window.addEventListener('popstate', callback) 照样可以实现功能

除了通过路由控制之外,页面元素肯定也必须能够对弹层显隐进行控制,例如点击某个元素弹起弹层,这样才贴合真实使用场景

同样的,由于弹层的显隐实际上是由路由的切换控制,所以页面内部想要改变弹层的显隐,也必须通过路由切换来完成:

changeVisible () {
  if (this.visible) {
    this.$router.go(-1)
  } else {
    this.$router.push('/physicsBack/footerModal')
  }
}
复制代码

由于这实际上是利用了系统的返回能力,所以无论你在浏览器上还是客户端 appwebview内,无论是使用屏幕返回还是物理返回键,都可以达到同样的效果

实现的效果如下:

做了个 Live Demo,感兴趣的可以亲自试下,代码也已经上传到 github

小结

本文所说的通过路由来模拟拦截返回键的能力,不仅仅可以应用在弹层例子上,其他跟返回相关的操作理论上都可以发挥想象,例如返回重定向,禁止用户退出页面(这是什么傻叉需求啊)

关注下面的标签,发现更多相似文章
评论