本文是內部的一次分享沉淀,偏向基礎但是涉及了一些有意思的細節,文筆有限,才疏學淺,文中若有不正之處,萬望告知。
前端的一大工作內容就是去兼容頁面在不同內核的瀏覽器,不同的設備,不同的分辨率下的行為,使頁面的能正常工作在各種各樣的宿主環境當中。
而本文的主題 -- 移動端開發的兼容適配與性能優化,就是希望能從一些常見的移動端開發問題出發,厘清 Web 移動端開發的前前后后,一些技術的發展過程,一些問題的優化手段以及給出一些常見的兼容性問題的解決方案。
什么是響應式設計
首先先聊聊響應式設計,這個與移動端開發有著密切的聯系。
響應式設計即是 RWD,Responsive Web Design。
這里百度或者谷歌一下會有各種各樣的答案。這里一段摘自知乎上我覺得很棒的一個答案:什么是響應式布局設計?[1]
根據維基百科及其參考文獻,理論上,響應式界面能夠適應不同的設備。描述響應式界面最著名的一句話就是“Content is like water”,翻譯成中文便是“如果將屏幕看作容器,那么內容就像水一樣”。
為什么要設計響應式界面
為什么要費神地嘗試統一所有設備呢?
即便是 PC 或 Mac 用戶,有數據顯示只有一半的人會將瀏覽器全屏顯示,而剩下的一般人使用多大的瀏覽器,很難預知;臺式機、投影、電視、筆記本、手機、平板、手表、VR……智能設備正在不斷增加,“主流設備”的概念正在消失;屏幕分辨率正飛速發展,同一張圖片在不同設備上看起來,大小可能天差地別;鼠標、觸屏、筆、攝像頭手勢……不可預期的操控方式正在不斷出現。
響應式界面的四個層次
同一頁面在不同大小和比例上看起來都應該是舒適的;同一頁面在不同分辨率上看起來都應該是合理;同一頁面在不同操作方式(如鼠標和觸屏)下,體驗應該是統一的;同一頁面在不同類型的設備(手機、平板、電腦)上,交互方式應該是符合習慣的。
響應式界面的基本規則
可伸縮的內容區塊:內容區塊的在一定程度上能夠自動調整,以確保填滿整個頁面
可自由排布的內容區塊:當頁面尺寸變動較大時,能夠減少/增加排布的列數
適應頁面尺寸的邊距:到頁面尺寸發生更大變化時,區塊的邊距也應該變化
能夠適應比例變化的圖片:對于常見的寬度調整,圖片在隱去兩側部分時,依舊保持美觀可用
能夠自動隱藏/部分顯示的內容:如在電腦上顯示的的大段描述文本,在手機上就只能少量顯示或全部隱藏
能自動折疊的導航和菜單:展開還是收起,應該根據頁面尺寸來判斷
放棄使用像素作為尺寸單位:用dp(對于前端來說,這里可能是rem)尺寸等方法來確保頁面在分辨率相差很大的設備上,看起來也能保持一致。同時也要求提供的圖片應該比預想的更大,才能適應高分辨率的屏幕
上面一段我覺得已經涵蓋了響應式設計的絕大部分,簡單總結起來,可以概括為:
媒體查詢,邊界斷點的規則設定(Media queries && break point)內容的可伸縮性效果(Flexibel visuals)流式網格布局(Fluid grids)主要內容呈現及圖片的高質量(Main content and high quality)
響應式 vs. 自適應
響應式設計是 Responsive Web Design(RWD),自適應設計是 Adaptive Web Design(AWD)。經常有人會將兩者混為一談,或者其實根本也區分不了所謂的響應式與自適應。
其實在我寫這篇文章的時候,我也無法很好的去區分兩者。
RWD 和 AWD 兩者都是為了適配各種不同的移動設備,致力于提升用戶體驗所產生的的技術。核心思想是用技術來使網頁適應從小到大(現在到超大)的不同分辨率的屏幕。通常認為,RWD 是 AWD 的子集。
RWD:Ethan Marcote 的文章是大家認為 RWD 的起源。他提出的 RWD 方案是通過 HTML 和 CSS 的媒體查詢技術,配合流體布局實現。RWD 傾向于只改變元素的外觀布局,而不大幅度改變內容。Jeffrey Zeldman 總結說,我們就把 RWD 定義為一切能用來為各種分辨率和設備性能優化視覺體驗的技術。
AWD:Adaptive Design 是 Aaron Gustafson 的書的標題。他認為 AWD 在包括 RWD 的 CSS 媒體查詢技術以外,也要用 Javascript 來操作 HTML 來更適應移動設備的能力。AWD 有可能會針對移動端用戶減去內容,減去功能。AWD 可以在服務器端就進行優化,把優化過的內容送到終端上。
一圖勝千言。
image
從定義上而言,RWD 是一套代碼,適用于所有屏幕。而 AWD 則是多端多套代碼。本文不會過多去糾結響應式與自適應區別,我覺得這兩者的本質都是致力于適配不同設備,更好地提升用戶體驗。
Quora - Responsive Design vs. Adaptive Design?[2]zhihu -- Responsive design 和 Adaptive design 的區別[3]
漸進增強 vs. 優雅降級
漸進增強(progressive enhancement):針對低版本瀏覽器進行構建頁面,保證最基本的功能,然后再針對高級瀏覽器進行效果、交互等改進和追加功能達到更好的用戶體驗。
優雅降級(graceful degradation):一開始就構建完整的功能,然后再針對低版本瀏覽器進行兼容。
區別:優雅降級是從復雜的現狀開始,并試圖減少用戶體驗的供給,而漸進增強則是從一個非常基礎的,能夠起作用的版本開始,并不斷擴充,以適應未來環境的需要。降級(功能衰減)意味著往回看;而漸進增強則意味著朝前看,同時保證其根基處于安全地帶。
漸進增強/優雅降級通常是 AWD 會牽扯到的另一個技術術語。本質上而言即是隨著屏幕的大小的改變,功能會一點一點增強。
也通常會用在一些高級 CSS3 屬性上,我們對一些 CSS 屬性進行特性檢測,甚至不進行特性檢測直接使用。后果是在支持它的網頁上該屬性正常展示,而不支持它的網頁該屬性不生效,但也不影響用戶的基本使用。
典型的例子是 CSS3 逐漸被大眾認可并被使用,PC端頁面開始由 IE678 向兼容性更好的IE9+,chrome,firefox瀏覽器轉變的時期。我們可以對頁面元素直接使用陰影,圓角等屬性。對于不支持它的低版本 IE 而言,沒有什么損失,而對于支持它的高級瀏覽器而言,帶給了用戶更好的交互視覺體驗,這就是漸進增強。
移動端屏幕適配方案
下面會針對一些具體的案例,展開講講。第一個是高保真還原設計稿,也就是如何適配移動端繁雜的屏幕大小。
通常而言,設計師只會給出單一分辨率下的設計稿,而我們要做的,就是以這個設計稿為基準,去適配所有不同大小的移動端設備。
在此之前,有一些基礎概念需要理解。
一些概念性的東西,大部分人很難一次性記住,或者記了又忘,我覺得記憶這個東西比較看技巧,比如關聯法,想象法,把這些生硬的概念與一些符合我們常識的知識關聯在一起記憶,往往能夠事半功倍。
設備獨立像素
以 iPhone6/7/8為例,這里我們打開 Chrome 開發者工具:
image
這里的 375 * 667 表示的是什么呢,表示的是設備獨立像素(DIP),也可以理解為 CSS 像素,也稱為邏輯像素:
設備獨立像素 = CSS 像素 = 邏輯像素
如何記憶呢?這里使用 CSS 像素來記憶,也就是說。我們設定一個寬度為 375px 的 div,剛好可以充滿這個設備的一行,配合高度 667px ,則 div 的大小剛好可以充滿整個屏幕。
物理像素
OK,那么,什么又是物理像素呢。我們到電商網站購買手機,都會看一看手機的參數,以 JD 上的 iPhone7 為例:
image
可以看到,iPhone7 的分辨率是 1334 x 750,這里描述的就是屏幕實際的物理像素。
物理像素,又稱為設備像素。顯示屏是由一個個物理像素點組成的,1334 x 750 表示手機分別在垂直和水平上所具有的像素點數。通過控制每個像素點的顏色,就可以使屏幕顯示出不同的圖像,屏幕從工廠出來那天起,它上面的物理像素點就固定不變了,單位為pt。
設備像素 = 物理像素
DPR(Device Pixel Ratio) 設備像素比
OK,有了上面兩個概念,就可以順理成章引出下一個概念。DPR(Device Pixel Ratio) 設備像素比,這個與我們通常說的視網膜屏(多倍屏,Retina屏)有關。
設備像素比描述的是未縮放狀態下,物理像素和設備獨立像素的初始比例關系。
簡單的計算公式:
DPR = 物理像素 / 設備獨立像素
我們套用一下上面 iPhone7 的數據(取設備的物理像素寬度與設備獨立像素寬度進行計算):
iPhone7’s DPR = iPhone7’s 物理像素寬度 / iPhone7's 設備獨立像素寬度 = 2
750 / 375 = 2 或者是 1334 / 667 = 2
可以得到 iPhone7 的 dpr 為 2。也就是我們常說的視網膜屏幕。
視網膜(Retina)屏幕是蘋果公司"發明"的一個營銷術語。蘋果公司將 dpr > 1 的屏幕稱為視網膜屏幕。
image
在視網膜屏幕中,以 dpr = 2 為例,把 4(2x2) 個像素當 1 個像素使用,這樣讓屏幕看起來更精致,但是元素的大小本身卻不會改變:
image
OK,我們再來看看 iPhone XS Max:
它的物理像素如上圖是 2688 x 1242,
image
它的 CSS 像素是 896 x 414,很容易得出 iPhone XS Max 的 dpr 為 3。
上面三個概念(CSS像素、設備獨立像素、DPR)是我覺得比較重要的,還有一些其他比較重要的概念 PPI、DPI 不影響后續的內容,可以自行去加深理解。
OK,到這里我們就完成了一個小的里程碑。我們通常說的H5手機適配也就是指的這兩個維度:
適配不同屏幕大小,也就是適配不同屏幕下的 CSS 像素適配不同像素密度,也就是適配不同屏幕下 dpr 不一致導致的一些問題
適配不同屏幕大小
適配不同屏幕大小,也就是適配不同屏幕下的 CSS 像素。最早移動端屏幕 CSS 像素適配方案是CSS媒體查詢。但是無法做到高保真接近 100% 的還原。
適配不同屏幕大小其實只需要遵循一條原則,確保頁面元素大小的與屏幕大小保持一定比例。也就是:按比例還原設計稿
假設我們現在拿到標注為 375*667 的大小的設計稿,其中一個元素的標注如下:
以頁面寬度為基準的話,那么,
元素的寬度為:209/375 = 55.73%元素的高度為:80/375 = 21.33%元素的上左右邊距依次計算...
這樣,無論屏幕的 CSS 像素寬度是 320px 還是 375px 還是 414px,按照等量百分比還原出來的界面總是正確的。
然而,理想很豐滿,現實很骨感。實現上述百分比方案的核心需要一個全局通用的基準單位,讓所有百分比展示以它為基準,但是在 CSS 中,根據CSS Values and Units Module Level 4[4]的定義:
百分比值總要相對于另一個量,比如長度。每個允許使用百分比值的屬性,同時也要定義百分比值參照的那個量。這個量可以是相同元素的另一個屬性的值,也可以是祖先元素的某個屬性的值,甚至是格式化上下文的一個度量(比如包含塊的寬度)。
具體來說:
寬度(width)、間距(maring/padding)支持百分比值,但默認的相對參考值是包含塊的寬度;
高度(height)百分比的大小是相對其父級元素高的大小;
邊框(border)不支持百分值;
邊框圓角半徑(border-radius)支持百分比值,但水平方向相對參考值是盒子的寬度,垂直方向相對參考值是盒子的高度;
文本大小(font-size)支持百分比值,但相對參考值是父元素的font-size的值;
盒陰影(box-shadow)和文本陰影(text-shadow)不支持百分比值;
首先,支持百分比單位的度量屬性有其各自的參照基準,其次并非所有度量屬性都支持百分比單位。所以我們需要另辟蹊徑。
rem 適配方案
在 vw 方案出來之前,最被大眾接受的就是使用 rem 進行適配的方案,因為 rem 滿足上面說的,可以是一個全局性的基準單位。
rem(font size of the root element),在 CSS Values and Units Module Level 3[5]中的定義就是, 根據網頁的根元素來設置字體大小,和 em(font size of the element)的區別是,em 是根據其父元素的字體大小來設置,而 rem 是根據網頁的跟元素(html)來設置字體大小。
flexible
基于此,淘寶早年推行的一套以 rem 為基準的適配方案:lib-flexible[6]。其核心做法在于:
根據設備的 dpr 動態改寫 <meta> 標簽,設置 viewport 的縮放給 <html> 元素添加 data-dpr 屬性,并且動態改寫 data-dpr 的值根據 document.documentElement.clientWidth 動態修改 <html> 的 font-size ,頁面其他元素使用 rem 作為長度單位進行布局,從而實現頁面的等比縮放
關于頭兩點,其實現在的 lib-flexible 庫已經不這樣做了,不再去縮放 Viewport,字體大小的設定也直接使用了 rem
hotcss
hotcss[7] 不是一個庫,也不是一個框架。它是一個移動端布局開發解決方案。使用 hotcss 可以讓移動端布局開發更容易。本質的思想與 flexible 完全一致。
對于 rem 方案的一些總結
使用 flexible/hotcss 作為屏幕寬度適配解決方案,是存在一些問題的:
動態修改 Viewport 存在一定的風險的,譬如通過 Viewport 改變了頁面的縮放之后,獲取到的 innerWidth/innerHeight 也會隨之發生變化,如果業務邏輯有獲取此類高寬進行其他計算的,可能會導致意想不到的錯誤;
到今天,其實存在很多在 flexible 基礎上演化而來的各種 rem 解決方案,有的不會對 Viewport 進行縮放處理,自行處理 1px 邊框問題。
flexible/hotcss 都并非純 CSS 方案,需要引入一定的 Javascript 代碼rem 的設計初衷并非是用于解決此類問題,用 rem 進行頁面的寬度適配多少有一種 hack 的感覺存在一定的兼容性問題,對于安卓 4.4 以下版本系統不支持 viewport 縮放(當然,flexible 處理 Android 系列時,始終認為其 dpr 為 1,沒有進行 viewport 縮放)
vw 適配方案
嚴格來說,使用 rem 進行頁面適配其實是一種 hack 手段,rem 單位的初衷本身并不是用來進行移動端頁面寬度適配的。
到了今天,有了一種更好的替代方案,使用 vw 進行適配 。
百分比適配方案的核心需要一個全局通用的基準單位,rem 是不錯,但是需要借助 Javascript 進行動態修改根元素的 font-size,而 vw/vh(vmax/vmin) 的出現則很好彌補 rem 需要 JS 輔助的缺點。
根據 CSS Values and Units Module Level 4:vw等于初始包含塊(html元素)寬度的1%,也就是
1vw 等于 window.innerWidth 的數值的 1%1vh 等于window.innerHeight 的數值的 1%
再以上面設計稿圖的元素為例,那么,
元素的寬度為:209/375 = 55.73% = 55.73vw元素的高度為:80/375 = 21.33% = 21.33vw元素的上左右邊距依次計算...
根據相關的測試,可以使用 vw 進行長度單位的有:
容器大小適配,可以使用 vw文本大小的適配,可以使用 vw大于 1px 的邊框、圓角、陰影都可以使用 vw內距和外距,可以使用 vw
簡單的一個頁面,看看效果,完全是等比例縮放的效果:
vw
CodePen Demo(移動端打開):使用 vw 進行頁面適配[8]
自動轉換插件
當我們使用 rem 作為長度單位的時,通常會有借助 Sass/Less 實現一個轉換函數,像是這樣:
// 假設設計稿的寬度是 375px,假設取設計稿寬度下 1rem = 100px
$baseFontSize: 100;
@function px2rem($px) {
@return $px / $baseFontSize * 1rem;
}
同理,在 vw 方案下,我們只需要去改寫這個方法:
// 假設設計稿的寬度是 375px
@function px2vw($px) {
@return $px / 375 * 100vw;
}
當然,我們還可以借助一些插件包去實現這個自動轉換,提高效率,譬如 postcss-px-to-viewport[9]
vw polyfill
vw 現在畢竟還是存在兼容問題的,看看兼容性:
image
其實已經覆蓋了絕大部分設備,那么如果業務使用了且又真的出現了兼容問題,應該怎么處理呢?有兩種方式可以進行降級處理:
CSS Houdini:通過CSS Houdini針對vw做處理,調用CSS Typed OM Level1 提供的 CSSUnitValue API。CSS Polyfill:通過相應的Polyfill做相應的處理,目前針對于 vw 單位的 Polyfill 主要有:vminpoly、Viewport Units Buggyfill、vunits.js和 Modernizr
對于 vw 方案的一些總結
vw 確實看上去很不錯,但是也是存在它的一些問題:
也沒能很好的解決 1px 邊框在高清屏下的顯示問題,需要自行處理由于 vw 方案是完全的等比縮放,在完全等比還原設計稿的同時帶來的一個問題是無法很好的限定一個最大最小寬度值,由于 rem 方案是借助 Javascript 的,所以這一點 rem 比 vw 會更加的靈活
當然,兩個方案現階段其實都可以使用甚至一起搭配使用,更多詳情可以讀讀:
再聊移動端頁面的適配[10]H5必知必會之像素級還原設計稿Responsive And Fluid Typography With vh And vw Units[11]使用VH和VW實現真正的流體排版[12]
1px線
上面說到使用 vw 適配屏幕大小方案,其中有一個缺點就是在 Retina 屏下,無法很好的展示真正的 1px 物理像素線條。
設計師想要的 retina 下 border: 1px,其實是 1 物理像素寬度,而不是 1 CSS 像素寬度,對于 CSS 而言:
在 dpr = 1 時,此時 1 物理像素等于 1 CSS 像素寬度;在 dpr = 2 時,此時 1 物理像素等于 0.5 CSS 寬度像素,可以認為 border-width: 1px 這里的 1px 其實是 1 CSS像素寬度,等于 2 像素物理寬度,設計師其實想要的是 border-width: 0.5px;在 dpr = 3 時,此時 1 物理像素等于 0.33 CSS 寬度像素,設計師其實想要的是 border-width: 0.333px
然而,并不是所有手機瀏覽器都能識別 border-width: 0.5px,在 iOS7 以下,Android 等其他系統里,小于 1px 的單位會被當成為 0px 處理,那么如何實現這 0.5px、0.33px 呢?
這里介紹幾種方法:
漸變實現使用縮放實現使用圖片實現(base64)使用SVG實現(嵌入 background url)
Retina 屏幕下 1px 線的實現[13]
圖片適配及優化
圖像通常占據了網頁上下載資源的絕大部分。優化圖像通常可以最大限度地減少從網站下載的字節數以及提高網站性能。
通常可以,有一些通用的優化手段:
消除多余的圖像資源盡可能利用 CSS3\SVG 矢量圖像替代某些光柵圖像謹慎使用字體圖標,使用網頁字體取代在圖像中進行文本編碼選擇正確的圖片格式為不同 DPR 屏幕提供最適合的圖片尺寸
本文重點關注如何在不同的 dpr 屏幕下,讓圖片看起來都不失真。
首先就是上述的第二點,盡可能利用 CSS3\SVG 矢量圖像替代某些光柵圖像。某些簡單的幾何圖標,可以用 CSS3 快速實現的圖形,都應該盡量避免使用光柵圖像。這樣能夠保證它們在任何尺寸下都不會失真。
其次,實在到了必須使用光柵圖像的地步,也是有許多方式能保證圖像在各種場景下都不失真。
無腦多倍圖
在移動端假設我們需要一張 CSS 像素為 300 x 200 的圖像,考慮到現在已經有了 dpr = 3 的設備,那么要保證圖片在 dpr = 3 的設備下也正常高清展示,我們最大可能需要一張 900 x 600 的原圖。
這樣,不管設備的 dpr 是否為 3,我們統一都使用 3 倍圖。這樣即使在 dpr = 1,dpr = 2 的設備上,也能非常好的展示圖片。
當然這樣并不可取,會造成大量帶寬的浪費。現代瀏覽器,提供了更好的方式,讓我們能夠根據設備 dpr 的不同,提供不同尺寸的圖片。
srcset 配合 1x 2x 像素密度描述符
簡單來說,srcset 可以根據不同的 dpr 拉取對應尺寸的圖片:
<div class='illustration'>
<img src='illustration-small.png'
srcset='images/illustration-small.png 1x,
images/illustration-big.png 2x'
style='max-width: 500px'/>
</div>
上面 srcset 里的 1x,2x 表示 像素密度描述符,表示
當屏幕的 dpr = 1 時,使用 images/illustration-small.png 這張圖當屏幕的 dpr = 2 時,使用 images/illustration-big.png 這張圖
srcset 屬性配合 sizes 屬性 w 寬度描述符
上面 1x,2x 的寫法比較容易接受易于理解。
除此之外,srcset屬性還有一個 w 寬度描述符,配合 sizes 屬性一起使用,可以覆蓋更多的面。
以下面這段代碼為例子:
<img
sizes = “(min-width: 600px) 600px, 300px"
src = "photo.png"
srcset = “photo@1x.png 300w,
photo@2x.png 600w,
photo@3x.png 1200w,
>
解析一下:
sizes = “(min-width: 600px) 600px, 300px"的意思是,如果屏幕當前的 CSS 像素寬度大于或者等于 600px,則圖片的 CSS 寬度為 600px,反之,則圖片的 CSS 寬度為 300px。
也就是 sizes 屬性聲明了在不同寬度下圖片的 CSS 寬度表現。這里可以理解為,大屏幕下圖片寬度為 600px,小屏幕下圖片寬度為 300px。(具體的媒體查詢代碼由 CSS 實現)
這里的 sizes 屬性只是聲明了在不同寬度下圖片的 CSS 寬度表現,而具體使圖片在大于600px的屏幕上展示為600px寬度的代碼需要另外由 CSS 或者 JS 實現,有點繞。
srcset = “photo@1x.png 300w, photo@2x.png 600w, photo@3x.png 1200w 里面的 300w,600w,900w 叫寬度描述符。怎么確定當前場景會選取哪張圖片呢?
當前屏幕 dpr = 2 ,CSS 寬度為 375px。
當前屏幕 CSS 寬度為 375px,則圖片 CSS 寬度為 300px。分別用上述 3 個寬度描述符的數值除以 300。
300 / 300 = 1600 / 300 = 21200 / 300 = 4
上面計算得到的 1、 2、 4 即是算出的有效的像素密度,換算成和 x 描述符等價的值 。這里 600w 算出的 2 即滿足 dpr = 2 的情況,選擇此張圖。
當前屏幕 dpr = 3 ,CSS 寬度為 414px。
當前屏幕 CSS 寬度為 414px,則圖片 CSS 寬度仍為 300px。再計算一次:
300 / 300 = 1600 / 300 = 21200 / 300 = 4
因為 dpr = 3,2 已經不滿足了,則此時會選擇 1200w 這張圖。
當前屏幕 dpr = 1 ,CSS 寬度為 1920px。
當前屏幕 CSS 寬度為 1920px,則圖片 CSS 寬度變為了 600px。再計算一次:
300 / 600 = .5600 / 600 = 11200 / 600 = 2
因為 dpr = 1,所以此時會選擇 600w 對應的圖片。
具體的可以試下這個 Demo:CodePen Demo -- srcset屬性配合w寬度描述符配合sizes屬性[14]
此方案的意義在于考慮到了響應性布局的復雜性與屏幕的多樣性,利用上述規則,可以一次適配 PC 端大屏幕和移動端高清屏,一箭多雕。
CSS 有個類似的屬性,image-set(),搭配使用,效果更佳。
了解更多細節,推薦看看:
Google Web Fundamentals -- Web Responsive Images[15]響應式圖片srcset全新釋義sizes屬性w描述符[16]
字體適配方案
字體大小
字體是很多前端開發同學容易忽略的一個點,但是其中也是有很多小知識點。
首先要知道,瀏覽器有最小字體限制:
PC上最小 font-size=12px手機上最小 font-size=8px
如果小于最小字體,那么字體默認就是最小字體。
其次,很多早期的文章規范都建議不要使用奇數級單位來定義字體大小(如 13px,15px...),容易在一些低端設備上造成字體模糊,出現鋸齒。
字體的選擇展示
在字體適配上面,我們需要從性能和展示效果兩個維度去考慮。
完整的一個字體資源實在太大了,所以我們應該盡可能的使用用戶設備上已有的字體,而不是額外去下載字體資源,從而使加載時間明顯加快。
而從展示效果層面來說,使用系統字體能更好的與當前操作系統使用的相匹配,得到最佳的展示效果。所以我們在字體使用方面,有一個應該盡量去遵循的原則,也是現在大部分網站在字體適配上使用的策略:
使用各個支持平臺上的默認系統字體。
兼顧各個操作系統
常見的操作系統有 Windows、Windows Phone、Mac OS X、iPhone、Android Phone、Linux。當然對于普通用戶而言,無須關注 Linux 系統。
下面就以 CSS-Trick[17] 網站最新的 font-family 為例,看看他們是如何在字體選擇上做到適配各個操作系統的
{
font-family:
system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,
Helvetica,Arial,
sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;
}
font-family 關鍵字
對于 CSS 中的 font-family 而言,它有兩類取值。
一類是類似這樣的具體的字體族名定義:font-family: Arial 這里定義了一個具體的字體樣式,字體族名為 Arial;
一類是通用字體族名,它是一種備選機制,用于在指定的字體不可用時給出較好的字體,類似這樣:font-family: sans-serif 。
其中,sans-serif 表無襯線字體族,例如, "Open Sans", "Arial" "微軟雅黑" 等等。
關于通用字體族名,在 CSS Fonts Module Level 3 -- Basic Font Properties[18] 中,定義了 5 個,也就是我們熟知的幾個通用字體族名:
serif 襯線字體族sans-serif 非襯線字體族monospace 等寬字體,即字體中每個字寬度相同cursive 草書字體fantasy 主要是那些具有特殊藝術效果的字體
新增通用字體族關鍵字
而在 CSS Fonts Module Level 4 -- Generic font families[19] 中,新增了幾個關鍵字:
system-ui 系統默認字體emoji 用于兼容 emoji 表情符號字符math 適用于數學表達式fangsong 此字體系列用于中文的(仿宋)字體。
我們看看用的最多的 system-ui。
system-ui
簡單而言,font-family: system-ui 的目的就是在不同的操作系統的 Web 頁面下,自動選擇本操作系統下的默認系統字體。
默認使用特定操作系統的系統字體可以提高性能,因為瀏覽器或者 webview 不必去下載任何字體文件,而是使用已有的字體文件。font-family: system-ui 字體設置的優勢之處在于它與當前操作系統使用的字體相匹配,對于文本內容而言,它可以得到最恰當的展示。
San Francisco Fonts
OK,簡單了解了 system-ui 字體族。但是像 -apple-system、BlinkMacSystemFont 沒有在最新的標準里出現。它們又代表什么意思呢?
在此之前,先了解下 San Francisco Fonts 。
San Francisco Fonts 又叫舊金山字體,是一款西文字體。隨著 iOS 9 更新面世,在 WatchOS 中隨 Apple Watch 一起悄然發售,并且還將在 Apple TV 上的新 tvOS 中使用。
San Francisco Fonts 在 iOS 系統上用于替代升級另外一款西文字體 Helvetica Neue。Apple 做了一些重要的改變,使其成為平臺上更好的, 甚至是完美的西文字體。
image
-apple-system/BlinkMacSystemFont
話說回來。正如每個前端開發人員都知道的那樣,將一個功能納入規范是一回事,將其納入瀏覽器又是另一回事。
幸運的是,system-ui 的普及很快。Chrome 和 Safari 都可以在各種平臺上完全支持它。只有 Mozilla 和 Windows 相對落后。
看看 system-ui 的兼容性,Can i Use -- system-ui[20](圖片截取日 2019-08-13):
image
仔細看上圖的最后兩行:
Supported as the -apple-system value (only on macOS and iOS)Supported as the BlinkMacSystemFont value (only on macOS)
考慮到不同平臺及向后兼容,在 macOS 和 iOS 上,我們需要使用 -apple-system 及 BlinkMacSystemFont 來兼容適配 system-ui 標準。
Segoe UI
Segoe UI 是 Windows 從 Vista 開始的默認西文字體族,只有西文,不支持漢字,屬于無襯線體。
它也表示一個系列而不是某一款單一字體。使用 font-family: Segoe UI 可以在 Windows 平臺及 Windows Phone 上選取最佳的西文字體展示。
Roboto
Roboto 是為 Android 操作系統設計的一個無襯線字體家族。Google 描述該字體為“現代的、但平易近人”和“有感情”的。
這個字體家族包含Thin、Light、Regular、Medium、Bold、Black六種粗細及相配的斜體。
總結一下
到此,我們可以總結一下了。以 CSS-Tricks[21] 網站的 font-family 定義為例子:
{
font-family:
system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,
Helvetica,Arial,
sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;
}
system-ui,使用各個支持平臺上的默認系統字體-apple-system, 在一些稍低版本 Mac OS X 和 iOS 上,它針對舊版上的 Neue Helvetica 和 Lucida Grande 字體,升級使用更為合適的 San Francisco FontsBlinkMacSystemFont,針對一些 Mac OS X 上的 Chrome 瀏覽器,使用系統默認字體segoe ui,在 Windows 及 Windows Phone 上選取系統默認字體Roboto,面向 Android 和一些新版的的 Chrome OSHelvetica,Arial,在針對不同操作系統不同平臺設定采用默認系統字體后,針對一些低版本瀏覽器的降級方案sans-serif,兜底方案,保證字體風格統一,至少也得是無襯線字體
上述 5 個字體族定義,優先級由高到底,可以看到,它們 5 個都并非某個特定字體,基本的核心思想都是選擇對應平臺上的默認系統字體。
涵蓋了 iOS、MAC OS X、Android、Windows、Windows Phone 基本所有用戶經常使用的主流操作系統。
使用系統默認字體的主要原因是性能。字體通常是網站上加載的最大/最重的資源之一。如果我們可以使用用戶機器上已有的字體,我們就完全不需要再去獲取字體資源,從而使加載時間明顯加快。
并且系統字體的優點在于它與當前操作系統使用的相匹配,因此它的文本展示必然也是一個讓人舒適展示效果。
當然,上述 font-family 的定義不一定是最佳的。譬如天貓移動端在 font-family 最前面添加了 "PingFang SC",miui,..必定也有他們的業務上的考慮。但是一些 fallback 方案向后兼容的思想都是一致的,值得參考學習。
更多的關于字體方面的細節知識,可以看看這幾篇文章:
你該知道的字體 -- font-family[22]Web 字體 font-family 再探秘[23]Using UI System Fonts In Web Design: A Quick Practical Guide[24]System Fonts in CSS[25]Apple’s San Francisco Font[26]
前端布局的兼容適配
前端工程師的一大工作內容就是頁面布局。無論在PC端還是移動端,頁面布局的兼容適配都是重中之重。在整個前端發展的歷程中,布局的方法也在不斷的推陳出新。
布局發展歷程
簡單來說,前端的布局發展歷程經歷了下面幾個過程:
表格布局 --> 定位布局 --> 浮動布局 --> flexbox布局 --> gridbox布局
每一種布局在特定時期都發揮了重要的作用,而每一種新的布局方式的出現,往往都是因為現有的布局方式已經在該時期已經無法很好的滿足開發者的需求,無法滿足越來越潮流的頁面布局的方式。
以 Flexbox 的出現為例子,在 Flexbox 被大家廣為接受使用之前。我們一直在使用定位+浮動的布局方式。像下面這個布局:
容器寬度不定,內部三個元素,均分排列且占滿整個空間,并且垂直居中。如果使用定位+浮動的布局方式,你無法很快想到最佳的解決方式。三個元素并排那么必然需要浮動或者絕對定位,容器寬度不定且中間元素始終居中,需要顧慮的方面就很多了。也許使用 text-align: justufy 可以 hack 實現,等等等等。
然而,使用 flexbox 布局的話,只需要:
.container {
display: flex;
justify-content: space-between;
align-items: center;
}
flexbox 的出現,一次性解決了流動布局,彈性布局,排列方式等多個問題。并且它是簡潔的,可控的。
再來看一個例子,水平垂直居中一個元素。使用 flexbox 也許是最便捷的:
.container {
display: flex;
}
.item {
margin: auto;
}
最便捷的垂直居中方式[27]
CSS Grid Layout
OK,flexbox 已經足夠優秀了,為什么 gird 網格布局的出現又是為什么?它解決了什么 flex 布局無法很好解決的問題?
看看下面這張圖:
flexbox 是一維布局,他只能在一條直線上放置你的內容區塊;而grid是一個二維布局。它除了可以靈活的控制水平方向之外,還能輕易的控制垂直方向的布局模式。對于上圖那樣的九宮格布局,它就可以輕而易舉的完成。
一圖以蔽之,flexbox:
gridbox:
圖片截取自陳慧晶老師在 2019 第五屆 CSS 大會上的分享 -- 新時代CSS布局[28]
在現階段,移動端布局應當更多使用 flexbox 去完成(相對那些還在使用 float 布局的),而考慮到未來頁面布局的推陳出新。對于 Grid 布局我們應當像前幾年對待 flexbox 一樣,重視起來,隨著兼容性的普及,Grid 布局也會慢慢成為主流。
最后
好了,本文到此結束,希望對你有幫助 :)
更多精彩技術文章匯總在我的 Github -- iCSS[29] ,持續更新,歡迎點個 star 訂閱收藏。
如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。