本文使用 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
仿寫:仿寫
如果解決了你遇到的問題,請隨手丟個 star 哈。
