大廠是怎么做移動端適配的

前言

2020年清明小假期,雖然新型冠狀病毒疫情得到了很好的控制,但是大家還是盡量避免出門,在家待著不能浪費(fèi)時間,讓我們重新思考一下移動端適配方案吧。

隨著Web技術(shù)的革新,移動端適配方案也在不斷的變化,網(wǎng)上有很多關(guān)于移動端適配的文章,說什么rem布局已經(jīng)過時,vm適配才是最好的適配方案。有這種理解的同學(xué)是錯誤的,任何適配方案都有它的優(yōu)缺點(diǎn),要結(jié)合自己的使用場景來進(jìn)行選擇。

文章先講一下幾種常見的適配方案,然后再看看幾個大廠(包括拍拍貸、小紅書、微博、美團(tuán)、B站、搜狐、京東、網(wǎng)易、餓了么、攜程、大眾點(diǎn)評、知乎、騰訊、陸金所)的移動端頁面都采用了什么樣的適配方案,最后討論下各個適配方法的適用場景和優(yōu)缺點(diǎn),如果有不對之處,希望能得到大佬們的指正。

移動端適配的重新思考

移動端適配就是用rem或vw?

答:并不是所有場景都適合用用remvw進(jìn)行適配。

  • vwrem適配的本質(zhì)是等比例縮放,讓頁面在不同屏幕尺寸下有類似于矢量圖片縮放的效果,保證了頁面元素之間的尺寸縮放比例和位置。
  • 這兩張適配方案適合視覺組件種類比較多,視覺設(shè)計對元素位置的相對關(guān)系依賴較強(qiáng)的移動端頁面,基本上大部分頁面都可以用著兩種方案進(jìn)行適配。
  • 但對于文本內(nèi)容較多,我們希望引導(dǎo)用戶沉浸在更多的內(nèi)容而不是更大的內(nèi)容的,這種等比例縮放的方案并不能滿足要求,我推薦直接使用px結(jié)合flex等布局方式進(jìn)行適配。

rem該拋棄了,使用vw不香么?

答:vw適配不是萬能的,最好與rem配合使用

  • 當(dāng)初之所以使用rem的方案流行開來正是因?yàn)樵谀菚rviewport units的瀏覽器支持程度不甚理想(IOS 8+, Android 4.4+ 參見viewport unitscaniuse)。而相比較之下rem就好多了(IOS 4.1+, Android 2.1+ 參見caniuse),所以對于vw,在當(dāng)時的大環(huán)境下前端想說愛你不容易。

  • 隨著前端技術(shù)的革新,最主要是各大瀏覽器廠商的給力,除Opera Mini全版本和IE低版本不支持之外,其他的瀏覽器基本上都已經(jīng)支持vw了,開始有人或者有團(tuán)隊在探討論在實(shí)際項(xiàng)目中的使用。雖然大漠老師在《再聊移動端頁面的適配》一文中提出的vw方案中使用viewport-units-buggyfill庫進(jìn)行兼容的做法,我個人更是不建議,由于這個庫使用了css content屬性進(jìn)行兼容處理,官方文檔中就指出了對部分瀏覽器的img標(biāo)簽有影響 ,需要全局引入一條css規(guī)則。且對于需要正常使用content的情況(如:圖標(biāo)字體)也會引起不可避免的沖突,另外也不支持偽元素的兼容。所以從我個人的角度來說,如果你一定要問我使用怎樣的vw適配方案,我會推薦給你上述兩種vw + rem的方案。

  • 雖然采用vw適配后的頁面效果很好,但是它是利用視口單位實(shí)現(xiàn)的布局,依賴視口大小而自動縮放,無論視口過大還是過小,它也隨著視口過大或者過小,失去了最大最小寬度的限制。

移動端適配方案

1. rem適配

rem適配的本質(zhì)是布局等比例的縮放,通過動態(tài)設(shè)置htmlfont-size來改變rem的大小。

viewport 配置
<!-- dpr = 1-->
<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">

上面把scale設(shè)置成固定1倍的視口的大小,也可以根據(jù)dpr的值縮放viewport,如下:

//下面是根據(jù)設(shè)備dpr設(shè)置viewport
var dpr = window.devicePixelRatio || 1
var scale = 1 / dpr

viewport.setAttribute(
    "content",
    "width=device-width" +
    ",initial-scale=" +
     scale +
    ", maximum-scale=" +
     scale +
    ", minimum-scale=" +
     scale +
    ", user-scalable=no"
)

有幾點(diǎn)注意:

  • viewport標(biāo)簽只對移動端瀏覽器有效,對PC端瀏覽器是無效的。
  • 當(dāng)縮放比例為100%時,邏輯像素 = CSS 像素寬度 = 理想視口的寬度 = 布局視口的寬度。
  • 單獨(dú)設(shè)置initial-scalewidth都會有兼容性問題,所以設(shè)置布局視口為理想視口的最佳方法是同時設(shè)置這兩個屬性。
  • 即使設(shè)置了user-scalable = no,在Android Chrome瀏覽器中也可以強(qiáng)制啟用手動縮放。
設(shè)置 rem 基準(zhǔn)值

核心代碼為:

// set 1rem = 邏輯像素(設(shè)備獨(dú)立像素) / 10
function setRemUnit () {
    var rem = document.documentElement.clientWidth / 10
    // 375/10 = 37.5
    docEl.style.fontSize = rem + 'px'
}
setRemUnit()
  • 將html節(jié)點(diǎn)的font-size設(shè)置為頁面clientWidth(布局視口)的1/10,即:1rem = 布局視口的1/10,
  • iphone6下:docEl.clientWidth=設(shè)備獨(dú)立像素(邏輯像素)= 布局視口寬度 = 理想窗口寬度 = 375。此時:1rem = 375/10 +px = 37.5px
postcss-pxtorem將單位轉(zhuǎn)化為 rem
module.exports = {
  plugins: {
    'autoprefixer': {
      browsers: ['Android >= 4.0', 'iOS >= 7']
    },
    'postcss-pxtorem': {
        rootValue: 37.5,
        propList: ['*', '!font-size'],
        selectorBlackList: ['van-circle__layer', 'ignore'],
    }
  }
}
  • rootValue是轉(zhuǎn)換px的基準(zhǔn)值,參考設(shè)備iPhone6,設(shè)備寬度375px
    規(guī)則:基準(zhǔn)值=當(dāng)前設(shè)備寬度的1/10
  • 基準(zhǔn)值設(shè)置代碼中,在iPhone6設(shè)備設(shè)置的html—>font-size 也為37.5px
  • 但是設(shè)計稿尺寸750px大小,所以在設(shè)計稿量取的尺寸使用時候需要除以2
rem布局的缺點(diǎn)

在響應(yīng)式布局中,必須通過js來動態(tài)控制根元素font-size的大小,也就是說css樣式和js代碼有一定的耦合性,且必須將改變font-size的代碼放在css樣式之前。

2. vw適配

vw是基于Viewport視窗的長度單位,這里的視窗(Viewport)指的就是瀏覽器可視化的區(qū)域,而這個可視區(qū)域是window.innerWidth/window.innerHeight的大小。用下圖簡單的來示意一下:

藍(lán)色區(qū)域就是 window.innerWidth 和 window.innerHeight

CSS Values and Units Module Level 3中和Viewport相關(guān)的單位有四個,分別為vw、vhvminvmax。

  • vw:是Viewport's width的簡寫,1vw等于window.innerWidth1%
  • vh:和vw類似,是Viewport's height的簡寫,1vh等于window.innerHeihgt1%
  • vminvmin的值是當(dāng)前vwvh中較小的值
  • vmaxvmax的值是當(dāng)前vwvh中較大的值

如果設(shè)計稿用750px寬度的,100vw = 750px,即1vw = 7.5px。那么我們可以根據(jù)設(shè)計圖上的px值直接轉(zhuǎn)換成對應(yīng)的vw值。如果不想自己計算,我們可以使用PostCSS的插件postcss-px-to-viewport,讓我們可以直接在代碼中寫px。

{
    loader: 'postcss-loader',
    options: {
        plugins: ()=>[
            require('autoprefixer')({
                browsers: ['last 5 versions']
            }),
            require('postcss-px-to-viewport')({
                viewportWidth: 375, //視口寬度(數(shù)字)
                viewportHeight: 1334, //視口高度(數(shù)字)
                unitPrecision: 3, //設(shè)置的保留小數(shù)位數(shù)(數(shù)字)
                viewportUnit: 'vw', //設(shè)置要轉(zhuǎn)換的單位(字符串)
                selectorBlackList: ['.ignore', '.hairlines'], //不需要進(jìn)行轉(zhuǎn)換的類名(數(shù)組)
                minPixelValue: 1, //設(shè)置要替換的最小像素值(數(shù)字)
                mediaQuery: false//允許在媒體查詢中轉(zhuǎn)換px(true/false)
            })
        ]
}

3. 搭配vw和rem

  • 給根元素大小設(shè)置隨著視口變化而變化的vw單位,這樣就可以實(shí)現(xiàn)動態(tài)改變其大小。
  • 限制根元素字體大小的最大最小值,配合body加上最大寬度和最小寬度。
// rem 單位換算:定為 75px 只是方便運(yùn)算,750px-75px、640-64px、1080px-108px,如此類推
$vm_fontsize: 75; // iPhone 6尺寸的根元素大小基準(zhǔn)值
@function rem($px) {
     @return ($px / $vm_fontsize ) * 1rem;
}
// 根元素大小使用 vw 單位
$vm_design: 750;
html {
    font-size: ($vm_fontsize / ($vm_design / 2)) * 100vw; 
    // 同時,通過Media Queries 限制根元素最大最小值
    @media screen and (max-width: 320px) {
        font-size: 64px;
    }
    @media screen and (min-width: 540px) {
        font-size: 108px;
    }
}
// body 也增加最大最小寬度限制,避免默認(rèn)100%寬度的 block 元素跟隨 body 而過大過小
body {
    max-width: 540px;
    min-width: 320px;
}

4. px 適配

就像開篇提到的,并不是說移動端就一定要使用相對長度單位,傳統(tǒng)的響應(yīng)式布局依然是很好的選擇,尤其在新聞,社區(qū)等可閱讀內(nèi)容較多的場景直接使用px單位可以營造更好地體驗(yàn)。px方案可以讓大屏幕手機(jī)展示出更多的內(nèi)容,更符合人們的閱讀習(xí)慣。

互聯(lián)網(wǎng)大廠的適配調(diào)研

1. rem適配例子

1.1 固定1倍vieport

注:下面描述的rempx的對應(yīng)關(guān)系是在設(shè)備獨(dú)立像素為375px(iPhone6/7/8)情況下。

拍拍貸m站首頁

  • 1rem = 20px
  • 最大基準(zhǔn)值為40px
  • 限制頁面寬度750px

小紅書

  • 1rem = 50px
  • 最大基準(zhǔn)值為60px
  • 字體和頁面都進(jìn)行縮放
  • 配合media query,限制body的最大寬度
@media screen and (min-width: 768px)
body {
    width: 450PX!important;
}

微博

  • 字體和頁面都進(jìn)行縮放
  • 基準(zhǔn)值是根據(jù)media query生成的
1.2 可縮放vieport

注:下面描述的rem與px的對應(yīng)關(guān)系是在設(shè)備獨(dú)立像素為375px(iPhone6/7/8)、viewport scale 0.5的情況下。

美團(tuán)

  • 1rem = 100px

B站主站

  • 1rem = 46.875px

搜狐

  • 1rem = 75px

2. vm適配例子

拍拍貸借款頁

  • 不限制頁面寬度
  • 無兼容性處理,個人不推薦

3. vm+rem適配例子

京東

  • 固定vieport,元素布局上使用rem單位
  • html元素的font-size使用vw + px fallback的形式
  • 當(dāng)頁面超過一定寬度時,根據(jù)media query設(shè)置font-sizepx,優(yōu)先級高于vw。
  • 限制頁面寬度為1080px
image.png

網(wǎng)易

  • 固定viewport,元素布局上使用rem單位
  • html元素的font-size使用vw + px fallback的形式
  • 使用media query設(shè)置根元素font-sizepx的值
  • 當(dāng)頁面超過一定寬度時,px單位的優(yōu)先級高于vw
  • 限制布局寬度為768px

餓了么

  • viewport進(jìn)行了縮放
  • html元素的font-size依然由px指定
  • 具體元素的布局上使用vw + rem fallbak的形式
  • 沒有限制布局寬度
  • css構(gòu)建過程需要插件支持,可參考這個插件:pandaGao/stylus-px-to-relative-unit

4. px 方案例子

攜程

  • 固定1倍vieport
  • 布局方案:px+flex+百分比
  • 設(shè)置body的最大寬度為max-width: 540px;

大眾點(diǎn)評

  • 元素較豐富,采用px+flex布局,適配效果很好

知乎

  • 追求閱讀體驗(yàn)的場景,使用px布局。

騰訊

  • 首頁主要內(nèi)容是新聞,為了更好的閱讀體驗(yàn),使用px布局。

陸金所

  • :root {font-size:10px;},并沒有根據(jù)屏幕的大小來設(shè)置不同的font-size
  • 存在問題:布局頁面設(shè)成1rem時候,在chrome瀏覽器上任然12px并不是10px
  • 布局中雖然用了rem單位,但其實(shí)還是絕對單位方案
  • 可能希望用戶在大屏手機(jī)上能看到更多內(nèi)容吧

總結(jié)

  • 新聞,社區(qū)等可閱讀內(nèi)容較多的場景:px+flex+百分比
  • 對視覺組件種類較多,視覺設(shè)計對元素位置的相對關(guān)系依賴較強(qiáng)的移動端頁面:vw + rem

以上只是自己的拙見以及自己這一兩年有關(guān)于移動端適配的一些探索,如果有不對之處,還請各路大神指正。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容