天天看點

分享手淘過年項目中采用到的前端技術

當你陪着家人嗑着瓜子,和家人一起看着春晚,順便拿着手淘參與春晚抽獎互動的時候,杭州還有兩百多程式員還奮戰在一線當中。現在年也過完了,獎也抽了,紅包也拿了。也該好好回來工作的時候了。這次很榮幸,自己能參與手淘過年項目(紅包開光和春晚互動項目)的項目中,雖然僅僅參與其中的部分工作,但事後感覺有些東西還是應該總結總結的,為之後的項目做準備。那麼簡單的來總結一下,我自己在參與項目中用到的一些前端技術。

這些技術其實也并不是什麼鮮為人知的技術棧,因為這些技術點已經出現很久了,隻不過大家習慣了自己的開發模式,加上項目時間緊,怕嘗試新的東西。事實上我自己也是如此,害怕使用這些技術點,給項目帶來其他的風險(本來項目時間就很緊),慶幸的是,接下來了到的一些東西,經住了項目的考驗,雖然當中踩過一些坑,但總算是無驚無險。

過年項目

手淘過年項目,事實上分為兩個,其中一個叫紅包開光,另一個是春晚抽獎的互動項目。

分享手淘過年項目中采用到的前端技術

上面兩張圖分别是紅包開光和春晚互動的主界面視覺圖。如果你參與過手淘過年互動的活動中,這兩個界面應該對您來說并不會太陌生。

經過團隊同學一起讨論,這次兩個項目都基于Vue來開發,Vue隻是一個JavaScript庫而以,選擇他并不沒有太多的主要原因,而是想讓團隊在今後的項目開發的時候,JavaScript庫能趨于統一,進而慢慢在項目中有所沉澱與積累。基于這個原因,我在其中主要做的事情,在這個腳手架中(也就是Vue-cli的基礎)添加了以下三個部分:

  • PostCSS插件
  • vw

    适配方案
  • iPhone X 适配

在互動腳手架中,目前已配置的PostCSS插件主要有:

PostCSS插件的配置

Webpack項目的

.postcssrc.js

最終的PostCSS插件的配置:

module.exports = {
    "plugins": {
        "postcss-import": {},
        "postcss-url": {},
        "postcss-aspect-ratio-mini": {}, 
        "postcss-write-svg": {
            utf8: false
        },
        "postcss-cssnext": {},
        "postcss-px-to-viewport": {
            viewportWidth: 750,     
            viewportHeight: 1334,   
            unitPrecision: 3,       
            viewportUnit: 'vw',  
            selectorBlackList: ['.ignore', '.hairlines'], 
            minPixelValue: 1,   
            mediaQuery: false   
        }, 
        "postcss-viewport-units":{},
        "cssnano": {
            preset: "advanced",
            autoprefixer: false,
            "postcss-zindex": false
        }
    }
}
           

對于這些PostCSS插件所起的作用和怎麼配置,在其對應的GitHub上都有詳細的描述。這裡簡要的描述一下,為什麼在我們的項目中會采用這些PostCSS的插件:

postcss-import

postcss-url

兩個主要是用于處理引入的檔案和資源路徑的處理以及工作模式。如果你的項目也使用的是Vue,并且配置了

vue-loader

,并且配置了相關的參數,那就就具有類似的功能。

autoprefixer

主要用來處理浏覽器的私有字首,這個已經是大家經常使用的一個PostCSS插件了。這裡需要提出的是,如果你的項目中使用了

postcss-next

cssnano

,那麼

autoprefixer

插件可以不引入,而且在

postcss-next

cssnano

兩者中選擇其一關閉

autoprefixer

,因為這兩個插件都內建了

autoprefixer

插件的特性。

postcss-cssnext

其實就是

cssnext

。該插件可以讓我們使用CSS未來的特性,其會對這些特性做相關的相容性處理。其包含的特性主要有:

有關于

cssnext

的每個特性的操作文檔,可以 點選這裡浏覽

cssnano

主要用來壓縮和清理CSS代碼。在Webpack中,

cssnano

css-loader

捆綁在一起,是以不需要自己加載它。不過你也可以使用

postcss-loader

顯式的使用

cssnano

。有關于

cssnano

的詳細文檔,可以

點選這裡擷取
注:由于

cssnano

preset

配置使用的是

advanced

,是以需要安裝

npm install cssnano-preset-advanced --save-dev

。另外

cssnext

cssnano

都具有

autoprefixer

的插件,是以在

cssnano

中将

autoprefixer

設定為

false

postcss-write-svg

插件主要用來處理移動端

1px

的解決方案。該插件主要使用的是

border-image

background

配合SVG繪制的矢量圖來做

1px

的相關處理。後續将會專門花一節的内容來介紹

postcss-write-svg

或者說怎麼能更好的使用SVG來處理移動端

1px

postcss-aspect-ratio-mini

主要用來處理元素容器寬高比。在項目當中很多地方會使用

img

object

或者

video

,那麼這個插件能更好的幫助我們完美處理寬高比的縮放。在實際使用的時候,具有一個預設的結構:

<div aspectratio>
    <div aspectratio-content></div>
</div>
           

在實際使用的時候,你可以把自定義屬性

aspectratio

aspectratio-content

換成相應的類名,比如:

<div class="aspectratio">
    <div class="aspectratio-content"></div>
</div>
           

我個人比較喜歡用自定義屬性,它和類名所起的作用是同等的。結構定義之後,需要在你的樣式檔案中添加一個統一的寬度比預設屬性:

[aspectratio] {
    position: relative;
}
[aspectratio]::before {
    content: '';
    display: block;
    width: 1px;
    margin-left: -1px;
    height: 0;
}

[aspectratio-content] {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
}
           

如果我們想要做一個

188:246

188

是容器寬度,

246

是容器高度)這樣的比例容器,隻需要這樣使用:

[w-188-246] {
    aspect-ratio: '188:246';
}
           

有一點需要特别注意:

aspect-ratio

屬性不能和其他屬性寫在一起,否則編譯出來的屬性隻會留下

aspect-ratio

的值,比如:

<div aspectratio w-188-246 class="color"></div>
           

編譯前的CSS如下:

[w-188-246] {
    width: 188px;
    background-color: red;
    aspect-ratio: '188:246';
}
           

編譯之後:

[w-188-246]:before {
    padding-top: 130.85106382978725%;
}
           

主要是因為在插件中做了相應的處理,不在每次調用

aspect-ratio

時,生成前面指定的預設樣式代碼,這樣代碼沒那麼備援。是以在使用的時候,需要把

width

background-color

分開來寫:

[w-188-246] {
    width: 188px;
    background-color: red;
}
[w-188-246] {
    aspect-ratio: '188:246';
}
           

這個時候,編譯出來的CSS就正常了:

[w-188-246] {
    width: 25.067vw;
    background-color: red;
}
[w-188-246]:before {
    padding-top: 130.85106382978725%;
}
           
這個現象也算是一個天坑吧。而這個坑是該插件自己帶來的,上面的處理方式隻是治标而不能治本。是以在使用該插件的時候,需要特别注意這個細節。

目前采用PostCSS插件隻是一個過渡階段,在将來我們可以直接在CSS中使用

aspect-ratio

屬性來實作長寬比。當然,如果你對

cssnext

熟悉的話,可以給其添加這樣的一個PR,将CSS原生的

aspect-ratio

屬性添加到

cssnext

特性當中,這樣隻要你使用

postcss-next

就可以忽略這個插件了。

剩下的

postcss-px-to-viewport

postcss-viewport-units

兩個PostCSS插件主要是用于

vw

适配方案,算是這次項目中必不可少的PostCSS插件。其中,

postcss-px-to-viewport

插件主要用來把

px

機關轉換為

vw

vh

vmin

vmax

這樣的視窗機關,也是

vw

适配方案的核心插件之一。

在配置中需要配置相關的幾個關鍵參數:

"postcss-px-to-viewport": {
    viewportWidth: 750,      // 視窗的寬度,對應的是我們設計稿的寬度,一般是750
    viewportHeight: 1334,    // 視窗的高度,根據750裝置的寬度來指定,一般指定1334,也可以不配置
    unitPrecision: 3,        // 指定`px`轉換為視窗機關值的小數位數(很多時候無法整除)
    viewportUnit: 'vw',      // 指定需要轉換成的視窗機關,建議使用vw
    selectorBlackList: ['.ignore', '.hairlines'],  // 指定不轉換為視窗機關的類,可以自定義,可以無限添加,建議定義一至兩個通用的類名
    minPixelValue: 1,       // 小于或等于`1px`不轉換為視窗機關,你也可以設定為你想要的值
    mediaQuery: false       // 允許在媒體查詢中轉換`px`
}
           

目前出視覺設計稿,我們都是使用

750px

寬度的,那麼

100vw = 750px

,即

1vw = 7.5px

。那麼我們可以根據設計圖上的

px

值直接轉換成對應的

vw

值。在實際撸碼過程,不需要進行任何的計算,直接在代碼中寫

px

,比如:

.test {
    border: .5px solid black;
    border-bottom-width: 4px;
    font-size: 14px;
    line-height: 20px;
    position: relative;
}
[w-188-246] {
    width: 188px;
}
           

編譯出來的CSS:

.test {
    border: .5px solid #000;
    border-bottom-width: .533vw;
    font-size: 1.867vw;
    line-height: 2.667vw;
    position: relative;
}
[w-188-246] {
    width: 25.067vw;
}
           

在不想要把

px

轉換為

vw

的時候,首先在對應的元素(

html

)中添加配置中指定的類名

ignore

hairlines

(

hairlines

一般用于設定

border-width:0.5px

的元素中):

<div class="box haspx"></div>
           

寫CSS的時候:

.ignore {
    margin: 10px;
    background-color: red;
}
.box {
    width: 180px;
    height: 300px;
}
           

編譯出來的CSS:

.box {
    width: 24vw;
    height: 40vw;
}
.ignore {
    margin: 10px; /*.box元素中帶有.ignore,在這個類名寫的`px`不會被轉換*/
    background-color: red;
}
           

上面解決了

px

vw

的轉換計算。

由于浏覽器對

vw

還具有一定的相容性,其在Android 4.4之下和iOS8以下的版本都存有一定的問題。為了讓

vw

vh

vmin

vmax

這些

viewport

機關能更好的使用。其相容方案就是使用

viewport

的polyfill:

Viewport Units Buggyfill

而在采用Viewport Units Buggyfill的時候,需要手動給使用

viewport

機關的樣式中添加其對應的Hack代碼,比如:

.box {
    top: 2vw;
    left: 1vw;

    content: 'viewport-units-buggyfill;top: 2vw;left: 1vw;';
}
           

如果每一個都這樣來做,那麼将是災難性的。幸運的是,可以使用

postcss-viewport-units

。其主要是給CSS的屬性添加

content

的屬性,配合

viewport-units-buggyfill

庫給

vw

vh

vmin

vmax

做适配的操作。

另一個坑,使用

postcss-viewport-units

将會給具有

content

屬性的元素造成一定的影響,比如你的項目中使用僞元素

::before

::after

或者僞類

:before

:after

之類。那麼使用該插件,會自動替換你原來的

content

内容,為了避免該現象,需要在

content

的屬性值末尾添加

!important

上面這些PostCSS插件是在這次項目中使用的,也将會在後面的項目中繼續使用,使用其主要原因是幫助我們解放雙手能更好的撸。或許你對其中一些插件有更好的使用心得,歡迎和我們一起分享,如果你有更好的插件,能幫助我們解放雙手,也歡迎分享給我們。

vw适配方案

vw

适配方案,主要是用于解決移動端布局的問題。事實上,在手淘,甚至到目前為止都還在使用Flexible的布局方案,用于适配移動端的各種終端。在15年雙11之後,寫了一篇《

使用Flexible實作手淘H5頁面的終端适配

》博文,将此方案分享給業内,而且該方案在業内快速的被複用和修改(原理是一樣的)。

Flexible的适合方案,在那個時期是非常強大的,想出這個方案的大神讓我膜拜已久。當然,事物是兩極的,他非常強大,但他也有自己的不足之處,特别是在

vw

得到更多的支援的時候,我覺得Flexible應該退出其曆史的使用(這是我自己YY的)。是以在17年年初我開始在探讨

vw

在移動端中的使用,經過一段時間的探讨和嘗試,我寫下了《

再聊移動端頁面的适配

》一文。

使用

vw

可以看到測試用例得到了衆多裝置的支援:

分享手淘過年項目中采用到的前端技術

大家看到衆多,或許會生疑,那麼還有不支援的将會是怎麼?特别是老闆跟我說,這次過年項目咱們使用

vw

來做适配布局吧。其實聽到這個消息,我自己是非常高興的,畢竟學習過的技術方案有較大的項目來驗證。心裡是美的,但也略感壓力,就害怕又會折騰出新的妖蛾子。想想都怕怕(^_^)。

衆所周知,浏覽器對

vw

 還具有一定的相容性,其在Android 4.4之下和iOS8以下的版本都存有一定的問題。為了讓

vw

vh

vmin

vmax

viewport

機關能更好的使用。需要考慮

viewport

機關在不支援的浏覽器(或裝置)做相應的處理。

為了能讓項目更安全,在決定過年項目中采用

vw

布局方案的時候,我就又再一次做了一個技術驗證,這次是基于Vue的Vue-cli腳手架的上來做的,畢竟我們的項目也要有Vue嘛。在這個腳手加上,我将上面介紹的PostCSS插件配置進去,特别是

postcss-px-to-viewport

postcss-viewport-units

兩個PostCSS插件和

讓我完美的解決了

vw

相容問題。而且讓開發者無感覺。他們不需要考慮怎麼處理相容,隻需要按着設計稿前行。

當然,完成這個技術方案的驗證,其中還是碰到一些坑的,幸好能像打老怪一樣,一個一個Fix。這裡就不闡述整個過程,如果你感興趣可以閱讀《

如何在Vue項目中使用vw實作移動端适配

》一文。接下來簡單的介紹一下

vw

相容方案處理方式。

vw相容方案

移動端使用

vw

布局,其相容方案就是使用

viewport

。使用

viewport-units-buggyfill

主要分以下幾步走:

引入JavaScript檔案

在你的HTML檔案,比如

index.html

中的

</head>

</body>

引入下面的JavaScript檔案:

<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script>
           

調用viewport-units-buggyfill的方法

同樣在HTML檔案中調用

viewport-units-buggyfill

的方法,比如:

<script>
window.onload = function () {
    window.viewportUnitsBuggyfill.init({
        hacks: window.viewportUnitsBuggyfillHacks
    });
}
</script>
           

更多的方法,可以閱讀其官網文檔。

Flexible項目無縫過渡到

vw

在前面釋出的部落格當中,有同學提到一個問題,如何能快速的将使用Flexible布局的項目無縫過渡到

vw

布局,剛好借這次項目的機會做了一個小測試。實作這個也并不複雜。簡單的描述一下其過程:

第一步

将Flexible項目中的

flexible.js

flexible.css

删除,并使用下面的

vw

的相容腳本:

<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script>
           

第二步

<head>

标簽添加

meta

标簽:

<meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no,viewport-fit=cover">
           
為了相容iPhone X的适配,在

meta

标簽中添加了

viewport-fit="cover"

參數處理。有關于這方面的可以閱讀《 iPhone X的缺口和CSS 》和《 iPhone X的Web設計 》。

第三步

添加前面提到的PostCSS插件的配置,當然,你可以隻添加處理

px

vw

vw

配合

的兩個PostCSS插件:

postcss-px-to-viewport

postcss-viewport-units

PostCSS插件配置有一個強大之處,不管你使用的是什麼腳手架,他的配置都非常的靈活,支援流行的配置工具,比如Webpack,gulp等。是以你不用擔心不好配置。因為在使用Flexible時,也需要

px2rem

的PostCSS插件配置。

第四步

重新編譯你的代碼,并在浏覽器中驗證一下編譯後的頁面。為什麼要這麼做呢?主要是因為

postcss-px-to-viewport

postcss-viewport-units

兩個插件會對

::before

:before

)、

::after

:after

img

content

做覆寫。如果你的項目中有使用到具有

content

屬性的元素,需要做一定的清理工作。目前這部分沒有較好的方式,我也咨詢過這兩個插件的作者,他們回報也沒找到更好的方案,隻能人肉處理,或者變相不使用

::before

::after

這樣的東東,添加額外的元素标簽來替代。如果你嘗試之後,發現有更好的方案,歡迎把你的方案分享給我們。

自從蘋果出了iPhone X的裝置,對于前端開發的同學而言,避免不了對其适配處理。而且這部分适配的處理相對而言是較為繁鎖的。我也一直在探尋從設計開始就能規避一些正常的适配問題。這部分内容正在整理,當其成熟之後再與大家分享。下面簡單的羅列一下自己對iPhone X适配的處理思路。

蘋果對于iPhone X上H5頁面的适配,提供了特殊屬性支援,包括meta标簽的

viewport

屬性值中加入

viewport-fit

和加入

constant(safe-area-inset-*)

env(safe-area-inset-*)

 ,這些屬性是與iOS11以上的所有iPhone機型(不僅僅包括iPhone X)都相關的,故以iOS版本為差別具體分析一下全屏下的H5頁面:

  • 針對iOS11.0以下系統:将不識别H5頁面

    meta

    标簽下的

    viewport-fit

    constant(safe-area-inset-*)/env(safe-area-inset-*)

    屬性。
  • 針對于iOS11.0-iOS11.1的系統:當設定了

    viewport-fit="cover"

    ,H5頁面會覆寫頁面安全區域全屏展示,但是這樣會帶來頁面元素會被“劉海兒”和底部Home Indicator遮擋問題,是以蘋果提供在CSS中設定

    constant(safe-area-inset-*)

    距離來規避遮擋問題。另外,頁面不加

    viewport-fit="cover"

    預設

    viewport-fit="contain/auto"

    ,也就是我們看到的頁面不能覆寫安全區域的情況,此時

    constant(safe-area-inset-*)

    的值都為 。是以在meta标簽的

    viewpoint

    中加

    viewport-fit="cover"

    時iOS10和iOS11下

    constant(safe-area-inset-*)

    值的表現是不一樣的。
  • 針對iOS11.2及iOS11.2以上的系統:

    constant()

    改成了

    env()

    。另外,iOS11.2新增了CSS function: 

    min()

    max()

    。例如:

    padding-left: max(12px, env(safe-area-inset-left));

    。在

    env(safe-area-inset-left)

    值因為Webview變化時值也可以做出相應變化,取

    12px

    env(safe-area-inset-left)

    的較大值。

總結如下圖:

分享手淘過年項目中采用到的前端技術

通過媒體查詢針對 iPhone X采用個性化樣式處理:

@media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) {
    /* iPhone X 獨有樣式寫在這裡*/
}
           

總結

最後感謝您花時間把這篇文章閱讀完。上面介紹的内容就是我自己在手淘過年項目中采用到的部分前端技術,稍作整理與大家分享。如果其中有不對之處,煩請路過的大嬸斧正。如果您在自己的項目中将采用上述提到的一些技術方案,踩到任何坑也歡迎一起探讨。

https://weibo.com/w3cplus

原文釋出時間:2018年02月27日

作者:大漠

本文來源:

w3cplus

  如需轉載請聯系原作者

繼續閱讀