我们先看一个实际例子:
点击菜品详情,按返回直接退出了店铺,这个例子还不是明显,在有很多小店铺的点单系统,连购物车都是一个纯组件,我进入购物车,按返回直接就退出页面了,更恶心有的没做记录,再次进去就空了
哪怕是各大厂的移动端应用也经常会看到这种尴尬的交互,这类的弹窗组件,在pc端,因为窗口足够大,通常也会有在右上角有关闭❌按钮,用户也习惯与这种操作,
但在vue移动端项目中,这类型,例如选择框/搜索框,比较主流的交互方式是,点击遮罩或者下方❌关闭,这种交互还算ok
但是有一些极端的情况下,我们可能需要整个弹窗是全屏幕,例如带搜索的选择组件,例如国籍/地址等
纯组件
最开始,使用纯组件,v-show展示
这种情况下,在用户的意识里,已经切换了页面 所以在安卓用户想返回的话,会下意识点物理返回 这时候就悲剧了,直接回到了上一页,而不是关掉当前激活的组件,测试直接给我提了一堆bug
子路由组件
基于这一点,最开始是用子路由思路做,这几乎完美解决保留完整的页面状态并跳转出一个新路由
只是依然存在一个严重副作用,所有子路由公用一个router-view,如果通过这个标签传参的话,子路由多了的情况下,难免会存在重名问题,当然是你自己设计的组件,通过前缀来解决也并非不可
并且随着越来越多的页面使用,就发现写法臃肿,这种状况持续大半年,随着业务量增加,需要嵌套子路由越来越多,大量重复的代码也非常难看。
插件式组件
终于开始着手做一些改动了
这过程考虑过的思路:
- 跟产品各种撒泼赖皮,把全屏的组件都设计成带遮罩的半屏组件,不给客户跳转了新页面的错觉,但始终是有些组件需要必须全屏的,不然可操作的区域是在太小
- 发现当输入框聚焦时,键盘弹起,按一次返回可以收起键盘;一顿操作之后,只有键盘弹起的情况,才可以能抵挡一次物理键返回,阻止键盘弹起本身也很难实现,无果。。。
最终确定的方案是插件式调用结路由哈希创建一条记录,主要代码如下:
let $vm
let $watcher
export function createDOM () {
if (typeof document === 'undefined') {
return
}
const MyComponent = Vue.extend(component)
const $vm = new MyComponent({
el: document.createElement('div')
})
document.body.appendChild($vm.$el)
return $vm
}
export function install () {
if (!$vm) {
$vm = createDOM()
Vue.prototype.$plugin = obj
}
}
export function show (options) {
const { cb } = options
if (cb) {
$vm.cb = cb
}
listening(hide)
const { path, query, params } = router.currentRoute
router.push({
path,
query,
params,
hash: 'pluginHash'
})
$watcher && $watcher()
$watcher = $vm.$watch('targetValue', (val) => {
if (val === false) {
hide()
$watcher && $watcher()
}
})
$vm.targetValue = true
}
export function hide () {
$vm.targetValue = false
$vm.$nextTick(() => {
$watcher && $watcher()
$watcher = null
if (window.location.hash.indexOf('pluginHash') !== -1) {
router.go(-1)
}
})
}
这是还有一点副作用的,和子路由一样会在history最前端创建了一条记录,尤其是微信7.0后,把前进后退按钮搬到下面了,如果用户点前进的话还是有点尴尬的,history并没有给我们提供类似pop的方法,但至少调用起来很方便,也不会有太多莫名的子路由嵌套。 大家有什么好的套路,也可以在评论区一起交流,
但就目前来看,越来越多的安卓机加入全面屏的阵营,取消了物理返回健,当全面屏成为主流,对于前端来说倒也省去很多这类的交互问题
后记
最近因为项目迁移升级,再加上搬家之类的私事,停下了小白路的更新,不过没关系,预计两周后会恢复正常更新,顺便预告下,小白路的第一个小游戏是扫雷已经完成了,还在优化中,下一次大可能与算法或单元测试相关,希望后面的能给大家带来更多干货