背景
大家在使用 v-if v-show
時有沒有遇到過這樣的情況叠荠,當我們 v-if = false
一個元素時午乓,其余元素的布局會收到影響法绵,舉個例子:
<div class="container">
<div class="blue"></div>
<div class="red"></div>
</div>
.container {
display: flex;
justify-content: space-between;
}
.blue {
width: 100px;
height: 100px;
background: blue;
}
.red {
width: 100px;
height: 100px;
background: red;
}
這段代碼目前是這樣顯示的,藍色的在左邊零截,紅色的在右邊
但是當我讓藍色的 div v-if = false
時反璃,如下圖所示
紅色的 div 就跑到了左邊昵慌,這顯然影響到了我們的布局,我們初衷是無論藍色塊如何顯示隱藏淮蜈,紅色塊永遠保持原來的位置斋攀。那如何解決這個問題呢,我們可以在藍色 div 外部再套一個 div梧田,隱藏時淳蔼,只隱藏內部 div,如下所示
<div class="container">
<div>
<div class="blue" v-if="false"></div>
</div>
<div class="red"></div>
</div>
但是這樣多了一層 html 嵌套裁眯,增加了復雜度鹉梨,而且還可能會打亂我們一開始的布局。
我們也可以使用不影響布局的隱藏穿稳,比如 opacity
俯画、translateX(9999px)
,但是官方?jīng)]有給到這樣的指令司草,所以我們只能使用動態(tài) style 去改變,但是這樣感覺不太優(yōu)雅泡仗,所以這時候埋虹,我們可以自己實現(xiàn)一個顯示隱藏的指令,原理就是改變 opacity
或 translateX(9999px)
娩怎。
talk is cheap, show me the code
vue-fake-hide
因為我們想讓元素隱藏時仍然占有原來的空間搔课,所以我們叫它假隱藏(手動滑稽):D
我們期望這個指令的使用方式是
<!-- 隱藏 -->
<div v-fake-hide="data"></div>
<!-- 顯示 -->
<div v-fake-hide="data"></div>
這里我們不對如何編寫指令進行過多的解讀,官方文檔介紹的很詳細截亦。
我們的目的是根據(jù) data
的改變去動態(tài)的改變元素的 opacity
爬泥。當 data
改變時柬讨,一定會觸發(fā) vue 組件的更新,這時候就會觸發(fā)指令的 update
跟 componentUpdated
鉤子函數(shù)袍啡。
update 是 VNode 更新時調用踩官,但是可能發(fā)生在其子 VNode 更新之前【呈洌可以把它理解為 beforeUpdate
componentUpdated 是 VNode 及其子 VNode 全部更新后調用蔗牡。
這里我們選擇用 compnentUpdated。也就是當 componentUpdated 觸發(fā)時嗅剖,我們判斷 date 的值辩越,從而控制元素的顯示隱藏。
但是這里要注意的是當鉤子第一次加載的時候并不會觸發(fā) componentUpdated信粮,但是第一次加載的我們卻需要判斷元素是否隱藏黔攒,否則初始狀態(tài)跟 data 無關。所以這時我們需要 bind 鉤子强缘。具體代碼如下
const fakeHide = {
bind: hide,
componentUpdated: hide
}
function hide(el, { value }) {
if (value) {
el.style.opacity = 0;
} else {
el.style.opacity = 1;
}
}
所以廢話一堆督惰,代碼就這么簡單。欺旧。姑丑。
到這就結束了嗎?當然沒有結束辞友,我們還有很多可以優(yōu)化的地方栅哀,沒有問題我們也要創(chuàng)造問題,we are bug maker :)
上面我們有提到可以通過 translateX(9999px)
來實現(xiàn)称龙,所以我們可以把它也融合到我們的指令里去留拾,我們期望這么使用
<!-- 默認使用 opacity 隱藏 -->
<div v-fake-hide="true"></div>
<!-- 使用 opacity 隱藏 -->
<div v-fake-hide.opacity="true"></div>
<!-- 使用 translate 隱藏 -->
<div v-fake-hide.translate="true"></div>
所以我們可以這么實現(xiàn)
export const fakeHideV2 = {
bind: hide,
componentUpdated: hide
}
function hide(el, { value, oldValue, modifiers }) {
if (value === oldValue) {
return;
}
modifiers.opacity && opacityHide(el, value);
modifiers.translate && translateHide(el, value);
}
function opacityHide(el, wetherHide) {
if (wetherHide) {
el.style.opacity = 0;
} else {
el.style.opacity = 1;
}
}
function translateHide(el, wetherHide) {
if (wetherHide) {
el.style.transform = 'translateX(9999999px)';
} else {
el.style.transform = 'none';
}
}
這里的 modifiers 就是指令的修飾符對象。我們通過使用 WeakMap 保存 dom 元素與 opacity 的映射
oh year 代碼已經(jīng)高達 30 行鲫尊,Niiiiiice
但是 we are bug maker痴柔,這里仍然有 bug。
如果元素原始的 opacity 是 0.5疫向,但是經(jīng)過我們的操作之后變成了 1咳蔚,不合適呀,transform 仍然存在這樣的問題搔驼,how do you do
我們可以在元素隱藏之前記錄一下原始的 opacity谈火,元素顯示的時候再拿出來,所以最終代碼如下
const fakeHideV2 = {
bind: hide,
componentUpdated: hide
}
const opacityMap = new WeakMap();
const translateMap = new WeakMap();
function hide(el, { value, oldValue, modifiers }) {
if (value === oldValue) {
return;
}
modifiers.opacity && opacityHide(el, value);
modifiers.translate && translateHide(el, value);
}
function opacityHide(el, wetherHide) {
if (wetherHide) {
const opacity = window.getComputedStyle(el).getPropertyValue('opacity');
opacityMap.set(el, opacity)
el.style.opacity = 0;
} else {
el.style.opacity = opacityMap.get(el);
}
}
function translateHide(el, wetherHide) {
if (wetherHide) {
const translate = window.getComputedStyle(el).getPropertyValue('translate');
translateMap.set(el, translate);
el.style.transform = 'translateX(9999999px)';
} else {
el.style.transform = translateMap.get(el);
}
}
YES 經(jīng)過我們的又一頓操作舌涨,我們的代碼已經(jīng)糯耍。。。還是 30 多行 :)
這波簡約又簡單的 假隱藏 你學廢了嗎
雖然很簡單温技,還是想厚著臉求個贊
代碼已上傳至 https://github.com/SupermanWY/vue-fake-hide
也可以直接通過 npm 在項目中使用
cnpm i vue-fake-hide -S
// 全局引入
import fakeHide from 'vue-fake-hide'
Vue.use(fakeHide)
// 組件內單獨注冊
import { fakeHideV2 } from 'vue-fake-hide'
export default {
dirctives: {
fakeHide: fakeHideV2
}
}