本文使用 Vue 做例子,其他框架或原生一样原理
先看效果
这个文章的启发是来自这几天跟别人讨论跨平台解决方案时候意外发现,许多多年前端经验的跨平台开发工程师,都在考虑组件切换引起的瞬间白屏以及组件返回,原组件显示还原问题。
误导思路
厉害的前端工程师总是可以解决问题,比如上面那个问题,我们拿 A、B 两个组件做实例。
- 组件切换,手写(transition)或使用 Vue-Router 来做 A、B 组件切换动画
- A 切换到 B,没有什么问题,无外乎动画优雅度问题
- B 回到 A,B 走了,没问题,但是 A 的出现便出现了问题
A 具体出现的问题,请允许我用语言描述,因为我后面的代码示例并非列表类型。
场景:列表到详情页面,列表上百个。
用户使用滚动,到下个或下下个屏幕的列表项,点击前往详情,详情浏览完毕后,点击返回或后退到列表页。
问题出现,列表内容得重新填充,还要把列表所在的滚动位置还原。
OK,有小伙伴说,记录下滚动的 scroll 即可。没错,但是如果这个页面有很多表格(有点扯),或者有其他各种交互变化,然后返回呢?一一去记录配置吗?
一一配置当然没有问题,但是工作量以及 bug 几率,啧啧啧……
分析原因
原因本身就是切换层级问题,简单来说就是:兄弟组件的切换,就是一个此消彼长的过程。
说人话!
好吧,就是说上面的 A 与 B 页面只能存活一个的意思。
A 出现,B 就不见;同样的,B 出现,A 页面也就消失了。
所以,从 B 返回 A 时候,A 需要重新渲染 DOM,从而导致相关的问题,也就是说,如果 A 是个简单页面,就不存在这个问题了。
推出结论
- 层级问题
- 解决层级问题的方案
- 所谓方案就是 B 出现时候 A 不消失
明白了么,这个原理,小伙伴。
话不多说,终于上代码:
Router
import Vue from 'vue'
import VueRouter from 'vue-router'
import Delegate from '../component/delegate/delegate.vue'
import Rule from '../component/rule/rule.vue'
import Rank from '../component/rank/rank.vue'
import More from '../component/more/more.vue'
import Login from '../component/login/login.vue'
import Empty from '../component/empty/empty.vue'
Vue.use(VueRouter)
export default new VueRouter({
routes: [
{path: '/empty', component: Empty, alias: '/'},
{path: '//delegate', component: Delegate},
{path: '/rule', component: Rule},
{path: '/rank', component: Rank},
{path: '/more', component: More},
{path: '/login', component: Login},
]
})
注意几方面东西。
- 斜杠代表了层级,因为我懒得写子级 route,所以出现
//delegate
来代表子子级。 - empty 作用,后面说,但是这里注意默认是 empty 即可,即
alias: '/'
。
animation
.push-enter {
transform: translateX(100%);
opacity: 0.8;
}
.push-enter-active {
transition: all 0.3s ease;
}
.push-enter-to, .push-leave {
transform: translateX(0);
opacity: 1;
}
.push-leave-active {
transition: all 0.3s ease;
}
.push-leave-to, .pop-enter {
transform: translateX(-50%);
opacity: 0.8;
}
.pop-enter-active {
transition: all 0.3s ease;
}
.pop-enter-to, .pop-leave {
transform: translateX(0);
opacity: 1;
}
.pop-leave-active {
transition: all 0.3s ease;
z-index: 999;
}
.pop-leave-to {
transform: translateX(100%);
}
动画效果,这里模仿的是移动端页面切换动画,有移动端经验小伙伴能看懂,就是类似 VC 与 Activity 切换那种出栈入栈效果。
但是截止位置,都没有解决上面的问题,没错,重点是下面。
放置一个空的子组件在当前页面上
说是说没解决,实际上逻辑上已经有那个意思了,回看 Router 那里,是不是有个 Empty,没错,这个就是在每个页面都显示出来的时候,放置在已有页面上的一个子组件,只不过大小为 0x0,位置随意,建议放在左上角,因为我们控制的 CSS 是修改 x 方向。
<template>
<div id="empty"></div>
</template>
<script>
export default {
}
</script>
<style>
</style>
我这里偷懒,所以就这样写了 empty.vue 了。
然后就是配置与 Empty 同级的兄弟组件了,也许你已经想到,这个兄弟组件的位置在右边,然后在屏幕外面等着呢(尽管目前可能没有渲染)。
所以,它的关键样式(公有)是这样的:
.page {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
配合上面的入场动画,就可以实现类似在右边划进来的效果了。
这里提一下,建议使用 absolute 来代替 fixed,虽然 fixed 看似一劳永逸,但是在不同平台上会有不同的问题。
到这里,基本原理说清楚了,下面简单总结:
- 做的不是兄弟组件视觉切换
- 做的是父子级视觉切换
- 但是实际上依然是兄弟组件切换
- 一个默认的 Empty 组件放在了已有的父组件上面,大小 0x0
- 切换的是这个 0x0 组件与其兄弟组件变化
实际效果
体验地址:全功能 Demo
仿写:仿写
Github
如果解决了你遇到的问题,请随手丢个 star 哈。