v-if v-show 的替代品——vue-fake-hide

背景

大家在使用 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;
}

這段代碼目前是這樣顯示的,藍色的在左邊零截,紅色的在右邊

image.png

但是當我讓藍色的 div v-if = false 時反璃,如下圖所示

image.png

紅色的 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)一個顯示隱藏的指令,原理就是改變 opacitytranslateX(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ā)指令的 updatecomponentUpdated 鉤子函數(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
  }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末革为,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子舵鳞,更是在濱河造成了極大的恐慌震檩,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件系任,死亡現(xiàn)場離奇詭異恳蹲,居然都是意外死亡,警方通過查閱死者的電腦和手機俩滥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門嘉蕾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人霜旧,你說我怎么就攤上這事错忱。” “怎么了挂据?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵以清,是天一觀的道長。 經(jīng)常有香客問我崎逃,道長掷倔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任个绍,我火速辦了婚禮勒葱,結果婚禮上,老公的妹妹穿的比我還像新娘巴柿。我一直安慰自己凛虽,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布广恢。 她就那樣靜靜地躺著凯旋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钉迷。 梳的紋絲不亂的頭發(fā)上至非,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音糠聪,去河邊找鬼睡蟋。 笑死,一個胖子當著我的面吹牛枷颊,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼夭苗,長吁一口氣:“原來是場噩夢啊……” “哼信卡!你這毒婦竟也來了?” 一聲冷哼從身側響起题造,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤傍菇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后界赔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丢习,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年淮悼,在試婚紗的時候發(fā)現(xiàn)自己被綠了咐低。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡袜腥,死狀恐怖见擦,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情羹令,我是刑警寧澤鲤屡,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站福侈,受9級特大地震影響酒来,放射性物質發(fā)生泄漏。R本人自食惡果不足惜肪凛,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一堰汉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧显拜,春花似錦衡奥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至譬淳,卻和暖如春档址,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背邻梆。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工守伸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浦妄。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓尼摹,卻偏偏與公主長得像见芹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蠢涝,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349