前言
最近寫了第三個移動端 H5 的專案,準備記錄下自己在 H5 專案中的一些實踐探索。移動端 H5 與 PC 端開發最大的區別之一,大概就是響應式佈局問題。
那麼下面我們就來聊聊移動端響應式佈局,了解他的來龍去脈,對現有的最佳解決方案探索吧。
問題全文將圍繞下面幾個問題進行論述和展開:
移動端 H5 相關頁面,相比 PC 端有哪些值得注意的點?移動端 H5 響應式佈局有哪些解決方案?什麼是 rem ?如何在專案中完美使用它?vh/vw 是最佳解決方案嗎?它有什麼優勢和缺陷?大型開源庫裡面常用的解決方案是什麼?怎樣快速搭建一套移動端佈局解決方案?由來概念什麼是 H5 技術?H5 這個命名本身是一個很不討巧的命名,咋一眼看上去認為是 HTML5,或者第 5 級標題的標籤,對一些人會造成了不小的誤解。
比如:我的一個某後端同事,談論到 H5 很簡單,HTML 之前我也學過一些,以後要是你請假,我來幫你寫。
我是一臉矇蔽,H5 === HTML?
再看看,搜尋引擎中 H5 是什麼?(引用來自谷歌詞條第一頁)
如此看來,將 H5 視作 HTML 的大有人在,而 H5 這個概念只在中國特有,所以對外國人來說他們也認為是 HTML, 所以,對於外國人和非這個領域的人來說,他們存在一樣的誤解。
目前的 H5 算是一個比較大的概念了,我認為的 H5 技術是一系列移動端 web 前端技術的集合 大致用一個韋恩圖表示如下
我們這裡只談 Web 前端中 H5 技術,H5 技術本身是用於移動端的 web 網頁。由於 App 本身有個 “ webview ” 的容器,在容器裡可以執行 Web 前端相關程式碼,由此 H5 和原生 App 結合又衍生出來了 Hybrid App 技術。
Hybrid App 技術大致原理這是我給公司同事普及 H5 知識繪製的影象。
移動端 web 和 PC web 的區別回想一下,在 PC 端我們是怎麼佈局的呢?
一般採用兩種方案,一種是 min-width 限制最小的寬度,瀏覽器寬度小於 min-width 就直接滾動。另外一種呢,就是留白。設定頁面一個基礎寬度,超出的部分留白。
但是這兩種解決方案在移動端可行嗎?
移動端手機的寬度,大多不一致,而且沒辦法進行視窗的縮放。讓移動端產生滾動,體驗更加糟糕,更別說和原生 App 效能相比較,基本頁面展示體驗就很差了。
那麼留白呢? 更不可行了,手機裝置本身寬度就小,再留白就基本看不了什麼了。
所以為了讓這種 Web 能夠像原生 App 一樣的體驗,就出現了 H5 技術。進一步衍生了 Hybird, 雖然比不上 App 效能,但是在熱更新 上佔有絕對優勢,有著原生無法替代的地方。
下面我們來就來實踐一下 H5 最基本的技術之一移動端響應式佈局。
實踐解決方案一:rem + pxToRem概念css 中用於計量的單位有兩種,一種是絕對單位,另一種是相對單位。
絕對單位對於絕對單位,一般來說常用的也就 px, 其他的可能列印需要用到。
相對單位對於相對單位來說,em 和 rem 屬於一對,vw 和 vh 屬於一對。
前一對相對於字型大小,區別在於 rem 相對於根字型,對於我們控制整體的大小相對容易些,所以我們可以使用它來控制整個頁面的縮放。
後一對,相對於視窗的大小,這將在下一個節中發揮主要作用。
原理監聽螢幕視窗的寬度,通過一定比例換算賦值給html的font-size。此時,根字型大小就會隨螢幕寬度而變化。將 px 轉換成 rem, 常規方案有兩種,一種是利用sass/less中的自定義函式 pxToRem,寫px時,利用pxToRem函式轉換成 rem。另外一種是直接寫px,編譯過程利用外掛全部轉成rem。這樣 dom 中元素的大小,就會隨螢幕寬度變化而變化了。實現動態更新根字型大小const MAX_FONT_SIZE = 420// 定義最大的螢幕寬度document.addEventListener('DOMContentLoaded', () => { const html = document.querySelector('html') let fontSize = window.innerWidth / 10 fontSize = fontSize > MAX_FONT_SIZE ? MAX_FONT_SIZE : fontSize html.style.fontSize = fontSize + 'px'})複製程式碼px 轉 rempxToRem 方案一$rootFontSize: 375 / 10;// 定義 px 轉化為 rem 的函式@function px2rem ($px) { @return $px / $rootFontSize + rem;}.demo { width: px2rem(100); height: px2rem(100);}複製程式碼pxToRem方案二在vue-cli3 中裝 postcss-pxtorem 外掛就可以了,其他平臺也是大致差不多的思路。
const autoprefixer = require('autoprefixer')const pxtorem = require('postcss-pxtorem')module.exports = { // ... css: { sourceMap: true, loaderOptions: { postcss: { plugins: [ autoprefixer(), pxtorem({ rootValue: 37.5, propList: ['*'] }) ] } } }}複製程式碼點選快速配置 H5 專案工程
繼續探索postcss-pxtorem外掛原始碼,檢視它實現的原理。
function createPxReplace (rootValue, unitPrecision, minPixelValue) { return function (m, $1) { if (!$1) return m; var pixels = parseFloat($1); if (pixels < minPixelValue) return m; var fixedVal = toFixed((pixels / rootValue), unitPrecision); return (fixedVal === 0) ? '0' : fixedVal + 'rem'; };}複製程式碼px變換成 rem 主要是這個函式,當然裡面有很多可配置的引數, 核心原理和我們方案一差不多。方便在於,不需要每次寫px都要加上一個函式,程式碼也清晰很多。
是不是所有元素 px 都要轉換成 rem呢?那可不一定哦,border 中的 px 不應該轉 rem,涉及到另外一個 1px 的問題,上一篇文章詳細論述過,避免 px 轉 rem,將 border 中的 px 大寫成 PX/Px/pX
1px 適配問題請移至 吃透移動端 1px
解決方案二:vh + vw原理vw 相對於視窗寬度的單位,隨寬度變化而變化。由此看來,方案一其實是方案二的一種 "Hack", 通過使用監聽實現了方案二的效果。
實現與 rem 類似做法,直接使用 postcss-px-to-viewport 外掛進行配置, 配置方式也是和 postcss-pxtorem 大同小異。
我們看看外掛的原理是不是也是一樣的呢?
function createPxReplace(opts, viewportUnit, viewportSize) { return function (m, $1) { if (!$1) return m; var pixels = parseFloat($1); if (pixels <= opts.minPixelValue) return m; var parsedVal = toFixed((pixels / viewportSize * 100), opts.unitPrecision); return parsedVal === 0 ? '0' : parsedVal + viewportUnit; };}複製程式碼果然呢,連方法名、變數名、程式碼邏輯,都一摸一樣哈哈哈!誰抄誰,我就不指出來啦!
其他解決方案上面方案均存在致命缺陷,不推薦使用它完成移動端佈局計算。
flex 與 rem 結合使用更佳
相容性上述兩種方案,相容性主要在於 rem,vh,vw 關鍵詞上
rem在移動端表現高達 100%,令人驚歎!
vh vw 表現還是比較令人滿意,低版本的 safari 情況下會有相容性問題,但不影響它會成為一種比較好的移動端佈局解決方案。
開源庫解決方案vant 元件庫vant 元件庫中,預設採用 px 做計量單位,如果需要使用 rem,直接使用外掛完美適配。
對於 vw 方案,vant 也是可以通過外掛將 px 轉成 vw,對於 vw 可能會存在一些坑點。
ant-design-mobile 元件庫ant-design-mobile 元件庫仍然使用 px 單位
@hd: 1px; // 基本單位// 字型尺寸// ---@font-size-icontext: 10 * @hd;@font-size-caption-sm: 12 * @hd;@font-size-base: 14 * @hd;@font-size-subhead: 15 * @hd;@font-size-caption: 16 * @hd;@font-size-heading: 17 * @hd;// 圓角// ---@radius-xs: 2 * @hd;@radius-sm: 3 * @hd;@radius-md: 5 * @hd;@radius-lg: 7 * @hd;@radius-circle: 50%;// 邊框尺寸// ---@border-width-sm: 1PX;@border-width-md: 1PX;@border-width-lg: 2 * @hd;複製程式碼與 vant 元件一樣,還是由開發者來決定到底用哪一種方案 這種把選擇權交給開發者,算是一種開源庫的最靈活的做法了。
關於 H5 一些踩坑總結 已更新