聊聊為什麼淘寶要提出「雙飛翼」佈局

木羽²發表於2017-11-13

前言

突然有一天,腦之裡不知怎地蹦出一個詞,「雙飛翼」,這是很久以前的淘寶提出的一種三欄佈局優化方案,然而,時間久了已經不記得(換句話說是不理解)為啥要提出這個佈局了,昨天在 SF 上發起了一個提問,但良久未有人答覆,幸得@王能全是誰 提醒,終於回想起「雙飛翼」的完整意義了。謹以此文同大家分享這段心路歷程。

聖盃 & 雙飛翼

說到「雙飛翼」就不得不提及「聖盃」,兩者均為三欄佈局的優化解決方案如下圖

常規情況下,我們的佈局框架使用以下寫法,從上到下,從左到右。

<header>header</header>
<section>
    <aside>left</aside>
    <section>main</section>
    <aside>right</aside>
</section>
<footer>footer</footer>複製程式碼

問題倒是沒什麼問題,然而,如果我們希望中部 main 部分優先顯示的話,是可以做佈局優化的。

因為瀏覽器渲染引擎在構建和渲染渲染樹是非同步的(誰先構建好誰先顯示),那麼將<section>main</section>部分提前即可優先渲染。

<header>header</header>
<section>
    <section>main</section>
    <aside>left</aside>
    <aside>right</aside>
</section>
<footer>footer</footer>複製程式碼

於是乎,國外的前輩就提出了「聖盃」佈局,目的就是通過 css 的方式配合上面的 DOM 結構,優化 DOM 渲染。

我們來簡要地瞭解一下「聖盃」佈局,這不是重點。

聖盃佈局

demo :jsfiddle.net/zwwill/px57…

<template>
<header>header</header>
<section class="wrapper">
    <section class="col main">main</section>
    <aside class="col left">left</aside>
    <aside class="col right">right</aside>
</section>
<footer>footer</footer>
</template>

<style>
/* 以下為簡碼,僅保留關鍵部分 */
header,footer {height: 50px;}
.wrapper {padding: 0 100px 0 100px; overflow:hidden;}
.col {position: relative; float: left;}
.main {width: 100%;height: 200px;}
.left {width: 100px; height: 200px; margin-left: -100%;left: -100px;}
.right {width: 100px; height: 200px; margin-left: -100px; right: -100px;}
</style>複製程式碼

使用了 relative 相對定位float(需要請浮動,此處使用 overflow:hidden; 方法)和 負值 margin ,將 left 和 right 部分「安裝」到 wrapper 的兩側,顧名「聖盃」。具體的思路我就不再做贅述了,網上到處都是解釋。

聖盃有問題

當然,正常情況下是沒有問題的,但是特殊情況下就會暴露此方案的弊端,如果將瀏覽器無線變窄,「聖盃」將會「破碎」掉。如圖,當 main 部分的寬小於 left 部分時就會發生布局混亂。

於是,淘寶軟對針對「聖盃」的缺點做了優化,並提出「雙飛翼」佈局。

雙飛翼佈局

demo :jsfiddle.net/zwwill/5xjy…

同樣的我們來看簡碼

<template>
<header>header</header>
<section class="wrapper">
    <section class="col main">
        <section class="main-wrap">main</section>
    </section>
    <aside class="col left">left</aside>
    <aside class="col right">right</aside>
</section>
<footer>footer</footer>
</template>

<style>
/* 以下為簡碼,僅保留關鍵部分 */
header,footer {height: 50px;}
.wrapper {padding: 0; overflow:hidden;}
.col {float: left;}
.main {width: 100%;}
.main-wrap {margin: 0 100px 0 100px;height: 200px;}
.left {width: 100px; height: 200px; margin-left: -100%;}
.right {width: 100px; height: 200px; margin-left: -100px;}
</style>複製程式碼

同樣使用了 float負值 margin,不同的是,並沒有使用 relative 相對定位 而是增加了 dom 結構,增加了一個層級。確實解決了聖盃佈局的缺陷。

為什麼要設計「雙飛翼」佈局

雙飛翼佈局表面上看是很優秀,但是細細想來,為什麼要多加一層 dom 樹節點,這豈不是增加了 css 樣式規則表和 dom 樹合併成佈局樹的計算量嗎?

好像絕對定位也可以解決這個問題

細想想,我們可以使用絕對佈局,將左右側邊欄定位到到兩側啊?好像也不會出現聖盃佈局的毛病?

demo :jsfiddle.net/zwwill/awwk…

<template>
<header>header</header>
<section class="wrapper">
    <section class="col main">main</section>
    <aside class="col left">left</aside>
    <aside class="col right">right</aside>
</section>
<footer>footer</footer>
</template>

<style>
/* 以下為簡碼,僅保留關鍵部分 */
header,footer { height: 50px;}
.wrapper { position: relative;}
.main { height: 200px; margin:0 100px;}
.left, .right{ width: 100px; height: 200px; position: absolute; top: 0;}
.left{ left: 0;}
.right{ right: 0;}
</style>複製程式碼

沒有使用 float(不用請浮動)也沒有 負值 margin ,僅僅使用了 absolute 絕對定位,好像更優秀呢?

但是細細想想,單純的絕對定位有一個問題,「高度不可控」,我們假設,如果 left 部分的高度高於 main ,是不是 left 沒有能力撐起整個 wrapper

「四不四」~~!

那麼我們再來看看雙飛翼和聖盃的情況

都是下圖。

「應戳死聽」~~!

那這麼看來,所有的方案都或多或少存在一些問題。綜合來看,不管 left, main, right 的大小高低如何,「雙飛翼」佈局都能正常顯示,嗯~~確實很優秀。

錘子和釘子

綜上所見,「雙飛翼」佈局更勝一籌。但是,這是一個「錘子和釘子」的問題,我們應該拿著釘子找錘子,而不是拿著錘子找釘子,因為,當你有了最大的錘子,看到什麼都是釘子。

唉~,我又在裝逼了。 \( ̄︶ ̄)/

說白了,就是,對症下藥,沒有最好的方案,只有最適合的。關於三欄佈局,我幫大家列出一個對照表,以便大家快速選擇。

優點 缺點
聖盃 結構簡單,無多餘 dom 層 中間部分寬度小於左側時佈局混亂
絕對定位 結構簡單,且無需清理浮動 兩側高度無法支撐總高度
雙飛翼 支援各種寬高變化,通用性強 dom 結構多餘層,增加渲染樹生成的計算量

以上為個人理解,如有不對或可補充之處,還請指點。

另外關於 CSS 佈局方案,和前端效能優化部分,移駕一下文章
多行多列類佈局方案總結
前端效能優化總結

轉載請標明出處
作者:木羽 zwwill
首發地址:github.com/zwwill/blog…

相關文章